共计 8959 个字符,预计需要花费 23 分钟才能阅读完成。
业务场景
对 ClickHouse 做多租户,是一个十分必要的场景。如果不加限度,用户可能会适度耗费数据库服务的资源,影响其余用户应用。本文尝试在硬件级别的隔离之外,给大家展现另外一种绝对便宜的办法。
本文先形容 ClickHouse 提供哪些资源限度的机制,再形容 GrowingIO 的实际。因为用户资源应用限度是用户治理的一种,本文也会退出用户治理的内容。
Hello World
个别通过 systemd 托管 ClickHouse,应用工具渲染模板得出配置文件,因为咱们的目标只是演示,所以简化了相干步骤和配置。
在 /etc/clickhouse 目录创立 2 个文件:
── conf
├── config.xml
└── users.xml
而后在任意目录运行:
clickhouse server --config=/etc/clickhouse/conf/config.xml
就让 ClickHouse 依照配置文件运行,同时 ClickHouse 监听配置文件,如有变动,不须要重启就能按新的配置运行。
The server tracks changes in config files… and reloads the settings for users and clusters on the fly (https://clickhouse.com/docs/e…)
这是简洁版的 config.xml 的配置 demo:
<?xml version="1.0"?>
<yandex>
<logger>
<level>trace</level>
<log>/tmp/log/clickhouse-server.log</log>
<errorlog>/tmp/log/clickhouse-server.err.log</errorlog>
<size>1000M</size>
<count>10</count>
</logger>
<query_log>
<database>system</database>
<table>query_log</table>
<partition_by>toYYYYMM(event_date)</partition_by>
<flush_interval_milliseconds>1000</flush_interval_milliseconds>
</query_log>
<tcp_port>9000</tcp_port>
<listen_host>127.0.0.1</listen_host>
<access_control_path>/tmp/ledzeppelin/</access_control_path>
<max_concurrent_queries>500</max_concurrent_queries>
<mark_cache_size>5368709120</mark_cache_size>
<path>./clickhouse/</path>
<users_config>users.xml</users_config>
</yandex>
留神到 <users_config>users.xml</users_config> 绑定了 config.xml 当前目录的 users.xml
<?xml version="1.0"?>
<yandex>
<profiles>
<default>
<readonly>1</readonly>
</default>
<pA>
<max_memory_usage>10G</max_memory_usage>
<max_memory_usage_for_user>10G</max_memory_usage_for_user>
<max_memory_usage_for_all_queries>10G</max_memory_usage_for_all_queries>
<max_query_size>1073741824</max_query_size>
<readonly>1</readonly>
</pA>
</profiles>
<users>
<zeppelin>
<!-- <password_sha256_hex></password_sha256_hex> -->
<password>password</password>
<networks>
<ip>::/0</ip>
</networks>
<profile>pA</profile>
<quota>qA</quota>
</zeppelin>
</users>
<quotas>
<qA>
<interval>
<!-- 以秒为单位 -->
<duration>10</duration>
<!-- 10 秒内只能查问一次 -->
<queries>2</queries>
<errors>0</errors>
<result_rows>0</result_rows>
<read_rows>0</read_rows>
<execution_time>0</execution_time>
</interval>
</qA>
</quotas>
</yandex>
能够初步看到 user, profile, quotas 之间的援用关系。这两个配置文件规定了用户 zeppelin 只能 10 秒查问 2 次
设置 Profile pA 为只读: <readonly>1</readonly>, 用户 zeppelin 援用了 pA, 当 zeppelin 执行写入语句的时候,会报错:
Quotas 和 Profile
Quotas 和 Settings Profiles 是 ClickHouse 用来管制用户资源的两种形式。
从第 2 节咱们能够获取这两种限度机制的初步印象。它们在 ClickHouse 的文档中的定义别离是:
Quotas allow you to limit resource usage over a period of time or track the use of resources.
援用
A settings profile is a collection of settings grouped under the same name.
设置 (settings) 也是一种限度。Profile 的定义并未突出”限度“的意思,但咱们齐全能够把它当作限度来用。
上图的 Profile demo 规定了用户”能读的行数“,max_rows_to_read,问题在于,这是一次能读的最大行数,还是用户存活的时候,总共能读的行数限度?答案是执行一次查问的时候,数据库管理工具能查问的最大行数。
A maximum number of rows that can be read from a table when running a query. (https://clickhouse.com/docs/e…)
能够看出 Profile 和 Quotas 的本质区别是 Profile 的设置与工夫无关,Profile 针对每一次查问,以最容易了解的 readonly 为例,”只读“不针对 10 秒 或 1 小时的用户行为,它针对的是每一次查问,对于每一次查问而言,Profile 的限度是实实在在的,而不像 Quota,这一秒还有 10 个配额,下一秒可能没有配额了,导致查问被抛弃。
为了进一步了解为什么要将 Quota 和 Interval 分割在一起,咱们须要联想到 QPS(Queries per second):
如图,当流量十分迅猛的时候,它会每个 Interval 的后期就把配额全副耗尽,导致剩下的工夫内无配额可用,进而导致断流,比方 A, B 区间。当流量绝对弛缓的时候,它在整个工夫区间的散布会比拟平均,比方 C 区间。
QPS 和 TIME 的积分就等于 Quota(配额)。
能够看出,ClickHouse 的 Quota 和 token bucket 限流办法有许多相似之处。令牌桶在每个时间段 (Interval) 生成肯定的配额,当配额耗尽,新的查问就要被抛弃。
从源码中能够看出,每次查问的时候,都去查看 (checkExceeded()) 是否超过配额
每个 interval 都有多种资源(resource_type), 比方 <query>1</query> 是一种 type, 查看最大库存 max,查看曾经应用的配额 used, 如果 used > max, 则报错。
SQL 驱动的访问控制
We recommend using SQL-driven workflow. Both of the configuration methods work simultaneously, so if you use the server configuration files for managing accounts and access rights, you can smoothly switch to SQL-driven workflow. (https://clickhouse.com/docs/e…)
ClickHouse 举荐应用 SQL 驱动的访问控制形式。第 2 节展现了”Server configuration files users.xml and config.xml“的管制形式,对于不简单的场景而言,曾经够用。配置文件的形式波及到平安问题,并且在减少用户的时候存在不灵便的问题,包含治理动态用户,动静 (长期) 用户。当初咱们将通过一系列的实际操作验证 SQL 驱动形式的可行性。
4.0 初步解决计划
- 通过 default 用户创立超级管理员 ledzeppelin 以及用户 zeppelin。(如果在 users.xml 定义了 zeppelin, 之后就无奈更改, 报错: Cannot update user zeppelin in users.xml because this storage is readonly)。
- 解除 default 的权限。
- 以超级管理员的身份, 通过 SQL 动静配置 zeppelin 资源
如图,普通用户 zeppelin 没有任何权限,它是被动的,不能执行任何创立”ClickHouse access entities“的操作;而 ledzeppelin 作为超级管理员则是被动的,它组装了 Profile, Quota, Role 等拜访实体,最初将角色赋予普通用户 zeppelin,从新定义 zeppelin 的属性,达到用户治理的目标。咱们也在这次实际中,从资源限度登程,最初扩大到更广泛的用户治理。
4.1 创立 zeppelin 用户
依照 ClickHouse 的举荐形式创立用户
4.1.1 给 default 用户 access_management 权限
<user>
<default>
<!-- 其余配置 -->
<access_management>1</access_management>
</default>
</user>
4.1.2 用 default 用户创立超级管理员 ledzeppelin 及普通用户 zeppelin
CREATE USER ledzeppelin;
GRANT ALL ON *.* TO ledzeppelin WITH GRANT OPTION;
CREATE USER zeppelin;
实际中必须设置明码, 比方
CREATE USER MJ IDENTIFIED WITH sha256_password BY 'qwerty';
登录:
clickhouse-client -u MJ --password qwerty
思考到隐衷, 能够将明码设置为环境变量
clickhouse-client -u MJ --password ${CLICKHOUSE_MJ_PASSWORD}
4.1.3 敞开 default 用户的 SQL 访问控制权限
出于对平安的思考, 咱们须要关掉 default 的权限避免出现制作另外一个超级管理员等隐患
首先退出 default 的登录, 留神不能通过 SQL 的形式更改 default 用户的属性, 不然会报错:
REVOKE ALL ON *.* FROM default
Query id: 7ab4a62a-62d1-4d0b-bbfe-c27d0e595f65
0 rows in set. Elapsed: 0.002 sec.
Received exception from server (version 21.10.2):
Code: 495. DB::Exception: Received from localhost:9000. DB::Exception: Cannot update user `default` in users.xml because this storage is readonly: Couldn't update user `default`. Successfully updated: none. (ACCESS_STORAGE_READONLY)
咱们只能通过批改 XML 的形式改 default 的配置
按 ClickHouse 的形式:
- 使 default 用户只读, 在 users.xml 设置一个只读 Profile
<profiles>
<!-- Profile that allows only read queries. -->
<readonly_profile>
<readonly>2</readonly>
<!-- 其余配置 -->
</readonly_profile>
</profiles>
- 更改 default 用户的 Profile:
<users>
<profile> readonly_profile</profile>
<!-- 其余配置 -->
</users>
- 并删掉这一行:
<access_management>1</access_management>
ClickHouse 会主动加载配置, 当初只剩下一个超级管理员: ledzeppelin
4.2 通过 SQL 动静治理资源
4.2.1 创立一个 Profile
以 readonly(Permissions for Queries | ClickHouse Documentation) 为例, 是因为这个属性容易测试, 0 的时候能够创立数据库, 1 和 2 的时候不能.
CREATE SETTINGS PROFILE IF NOT EXISTS z_profile
SETTINGS
readonly = 2
READONLY
4.2.2 创立一个 Quota
10 秒内只能查问一次
CREATE QUOTA IF NOT EXISTS z_quota
FOR INTERVAL 10 second
MAX queries 1
4.2.3 创立 Role
CREATE ROLE IF NOT EXISTS z_role
4.2.3.1 赋予 Role 权限
GRANT SELECT ON db.* TO z_role;
容许 z_role 拜访 db 数据库,留神到,咱们并不是赋予某个用户权限,而是赋予角色 (Role) 权限,而这一角色能够赋予其余用户。通过这种形式,咱们在权限和用户之间建设了一个形象层。
4.2.4 创立一个垃圾回收 Role
CREATE ROLE IF NOT EXISTS gc_role
作用是用来绑定不必的 profiles, quotas
4.2.5 Role 绑定 Profile, Quota
ALTER SETTINGS PROFILE z_profile TO z_role
ALTER QUOTA z_quota TO z_role;
4.2.6 利用到用户 zeppelin
查看 zeppelin 有绑定了哪些 role
SELECT * FROM system.role_grants WHERE user_name LIKE 'zeppelin'
如果有, 假如是 old_role, 就先解绑
REVOKE old_role FROM zeppelin
绑定新的角色:
GRANT z_role TO zeppelin
4.2.7 必须 REVOKE(重要)
咱们的构想是, profile 和 quota 绑定 role, role 绑定用户, 如果须要更改用户的配置, 有两种办法
- 换一个 role
- 改以后 role 的 profile/quota
第二种办法有问题, 因为改了 profile 之后, 并不会间接刷新用户的配置(可能是 ClickHouse 的 bug, 咱们还在验证中), 这时候就须要将 用户的 role revoke 下来, 再挂上去
保险起见, 应该依照以下范式操作:
- revoke z_role from zeppelin
- 批改 z_role 下的 profile 或 quota
- grant z_role from zeppelin
或:
- 批改 z_role 下的 profile 或 quota
- revoke z_role from zeppelin
- grant z_role from zeppelin
4.2.8 绑定一个新的 Quota
改为 5 秒一次查问
CREATE QUOTA IF NOT EXISTS z_quota_5
FOR INTERVAL 5 second
MAX queries 1
将原来的 z_quota 绑定到 gc_role
ALTER QUOTA z_quota TO gc_role;
绑上新 quota:
ALTER QUOTA z_quota_5 TO z_role;
刷新:
revoke z_role from zeppelin;
grant z_role to zeppelin;
查看:
SELECT
name,
apply_to_list
FROM system.quotas
WHERE name LIKE 'z_quota_5'
Query id: dc5b2ece-c792-4585-95d8-8b4275631664
┌─name──────┬─apply_to_list─┐
│ z_quota_5 │ ['z_role'] │
└───────────┴───────────────┘
1 rows in set. Elapsed: 0.004 sec.
4.2.9 绑定一个新的 Profile
CREATE SETTINGS PROFILE IF NOT EXISTS z_profile_permission_type_0
SETTINGS
readonly = 0
READONLY
更改 profile 的绑定对象, 先把 z_profile 绑定到 gc_role
ALTER SETTINGS PROFILE z_profile TO gc_role
将新的 z_profile_permission_type_0 绑到 z_role
ALTER SETTINGS PROFILE z_profile_permission_type_0 TO z_role
刷新配置:
revoke z_role from zeppelin;
grant z_role to zeppelin;
SELECT
name,
apply_to_list
FROM system.settings_profiles
Query id: 51d1c2de-ced7-4558-a164-020a76a53d97
┌─name────────────────────────┬─apply_to_list─┐
│ readonly │ [] │
│ default │ [] │
│ z_profile │ ['gc_role'] │
│ z_profile_permission_type_0 │ ['z_role'] │
└─────────────────────────────┴───────────────┘
5.SQL 驱动设置长久化
本地存储 / 本地长久化,和数据目录的 path 一样。依据 Server Settings | ClickHouse Documentation,通过 SQL 创立的属性存储在 access_control_path, 默认值为 /var/lib/clickhouse/access/
Path to a folder where a ClickHouse server stores user and role configurations created by SQL commands.
设置地位: config.xml
<yandex>
...
<access_control_path>/var/lib/clickhouse/access/ledzeppelin/</access_control_path>
...
</yandex
查看目录:
use/access/ledzeppelin
╰─➤ ls
094e5b76-1b4d-97a5-43cd-bbb3ccceb688.sql 9c492851-bf60-7753-7c14-c05f4be46b8e.sql
2949a246-848f-fc07-f1b9-a3f7246b31a0.sql f340165d-0ec4-f90e-fb55-74c8d0cfd7ae.sql
394e47fd-b701-d017-6857-0d8744d830b1.sql quotas.list
3d44c8a1-01b3-6f68-eff7-1e20a9080296.sql roles.list
3f447b54-b515-aa93-bacc-1a649aeac19c.sql row_policies.list
674912da-5593-e497-c5d8-d058e916c7a5.sql settings_profiles.list
6c44ad5c-4354-c154-048e-8e293ff427ab.sql users.list
另外, 留神到重启 ClickHouse 之后, ClickHouse 从 access_control_path 复原配置:
如果删掉这些文件,那么重启 ClickHouse 之后,之前通过 SQL 创立的 ” 拜访实体 ”(ClickHouse Access Entities) 就会隐没。
论断
本文从 ClickHouse 用户资源隔离开始,扩大到用户 ClickHouse 用户治理。咱们通过 Quotas 和 Profile 实现了资源隔离的业务;在用户治理中,咱们应用了角色 (Role),创立了一个新的形象层,这样一来,咱们就不须要在治理用户的时候间接操作权限,而是将角色作为一种组件赋予用户。角色也齐全能够封装资源。咱们能够通过配置文件或者 SQL 的形式部署这些拜访实体,后者是 ClickHouse 提倡的形式。最初,本文只波及 ClickHouse 用户治理的冰山一角,心愿抛砖引玉,对大家有所帮忙。
参考
- https://altinity.com/blog/goo…
- https://clickhouse.com/docs/e…
- Quotas:https://clickhouse.com/docs/e…
- Settings Profiles:https://clickhouse.com/docs/e…
- ClickHouse 的举荐形式:https://clickhouse.com/docs/e…