vault backup: 2026-07-04 20:23:48

This commit is contained in:
2026-07-04 20:23:48 +08:00
parent a5e418c701
commit ba57cfb9a2
7 changed files with 1644 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
---
tags: [xinfra, preview, infra]
create time: 2026-07-04 12:00
---
# XINFRA 预习总览
## 概述
xininfra 是公司的统一基础设施平台,以**七机房容器资源池化**为核心,配套 CI/CD 双引擎、数据库平台、WAF 安全态势、大内网互联和多云成本看板。本系列笔记覆盖开营前需要了解的全部技术栈,每篇聚焦一个工具/方向的核心概念和基本用法。
## 知识地图
```mermaid
graph LR
subgraph 底层
K8s[K8s / RKE2]
end
subgraph 容器管理
Wayne[Wayne 多集群管理]
end
subgraph 数据层
CloudDM[CloudDM SQL 审核]
CacheCloud[CacheCloud Redis 管理]
end
subgraph 自动化部署
Ansible[Ansible Playbook]
end
K8s --> Wayne
Wayne --> CloudDM
Wayne --> CacheCloud
CloudDM -.-> Ansible
CacheCloud -.-> Ansible
```
> [!info] 如何使用这组笔记
> 预习阶段**不必深读源码**。重点理解每个工具的:①定位(解决什么问题)②基本架构(有哪些组件)③典型使用流程(如部署一个服务的完整链路)。后续在实习中遇到具体问题时再回溯对应笔记。
## 快速索引
| # | 笔记 | 一句话定位 |
|---|------|-----------|
| 1 | [[technical/xinfra-preview/k8s-rke2-fundamentals]] | 容器编排底层基础 |
| 2 | [[technical/xinfra-preview/wayne-overview]] | 多集群容器管理和发布入口 |
| 3 | [[technical/xinfra-preview/cloud-dm-overview]] | 数据库 SQL 审核平台 |
| 4 | [[technical/xinfra-preview/cachecloud-overview]] | Redis 实例管理平台 |
| 5 | [[technical/xinfra-preview/ansible-playbook-basics]] | 服务自动化部署工具 |
@@ -0,0 +1,515 @@
---
tags: [ansible, playbook, infra, automation, iaas]
create time: 2026-07-04 13:30
---
# Ansible Playbook 基础
## 概述
Ansible 是一个无 Agent 的自动化运维工具,通过 SSH 协议远程管理主机。**Playbook** 是 Ansible 的核心概念——用 YAML 描述的自动化任务编排文件。xinfra 的进阶目标中要求使用 Ansible Playbook 部署 MySQL Server、PostgreSQL 和 Redis Cluster,这是实现数据库层**基础设施即代码(IaC)**的关键技能。
## 核心概念
### 工作原理
```mermaid
graph LR
Controller[控制节点 / Laptop or CI Host]
SSH[SSH 连接]
Nodes[(被控主机列表)]
Controller -->|读取 Playbook YAML| SSH
SSH -->|推送临时模块脚本| Nodes
Nodes -->|返回 JSON 结果| Controller
```
关键特点:
- **无 Agent**:不需要在被控机器上安装任何软件,仅依赖 Python + SSH
- **幂等性(Idempotent)**:同一个 Playbook 执行多次效果相同,不会重复创建或修改
- **模块化**:每个操作封装为独立模块(如 `ansible.builtin.shell`、`ansible.builtin.yum`、`ansible.builtin.template`)
### 特权提升(Become)
`become` 是 Ansible 的 **跨平台权限提升机制**,允许非 root 用户登录主机后切换到目标账户执行任务。不改变初始 SSH 连接会话——临时模块脚本仍通过原始身份推送,但实际运行时切换身份。
```yaml
# Playbook 级别 — 所有任务以 root 执行
- hosts: all_mysql_servers
become: yes
# Task 级别 — 精细控制
- name: Run command as postgres user
ansible.builtin.command: /usr/bin/pg_dump mydb
become: yes
become_user: postgres
become_method: sudo # su | doas | pfexec (OpenSolaris)
become_flags: '-s /bin/sh' # 额外标志位
```
| 参数 | 作用 | 示例值 |
|------|------|--------|
| **become** | 是否启用权限提升 | `yes` / `true` |
| **become_user** | 切换的目标用户 | `root`, `postgres`, `apache` |
| **become_method** | 底层实现方式 | `sudo`(默认), `su`, `pfexec` |
| **become_flags** | 传递给底层方法的附加参数 | `-s /bin/sh` |
> [!warning] 安全注意事项
> - **密码不能明文存储**:敏感凭据应通过 Ansible Vault 加密(见延伸阅读链接)
> - **Pipelining**:在 `/etc/ansible/ansible.cfg` 中开启 `pipelining = True`,可避免临时文件写入磁盘,减少安全风险
> - **每台主机仅能启用一种方法**:同时配置 `sudo` + `su` 会导致冲突
> - **最佳实践**:SSH 连入时已有较高权限 → 按需向下切换,比全程用 root 更安全
### Playbook 结构骨架
```yaml
- hosts: all_mysql_servers
become: yes # 提升 root 权限执行
vars: # 变量定义
mysql_port: 3306
mysql_data_dir: /data/mysql
roles: # 引用角色
- common-setup # 系统级准备
- install-mysql # MySQL 安装
tasks: # 显式任务列表
- name: Ensure MySQL is running
ansible.builtin.service: # 推荐写全称(命名空间规范)
name: mysqld
state: started
notify: restart mysql # 触发 handler
handlers: # 响应通知动作
- name: restart mysql
ansible.builtin.service:
name: mysqld
state: restarted
```
### 关键概念对照
| 概念 | 作用 | 类比 |
|------|------|------|
| **Hosts** | 指定目标机器组(Inventory 中定义) | SSH target |
| **Tasks** | 按顺序执行的操作 | 脚本命令 |
| **Roles** | 可复用的任务集合 | 函数库 |
| **Handlers** | 监听通知的触发器(常用于重启服务) | 事件回调 |
| **Modules** | 单功能执行单元 | 子命令 |
| **Inventory** | 被控主机清单 | SSH config 中的 host |
### 变量优先级体系(由低到高)
```yaml
# 变量加载顺序(低 → 高):
# 1. host_vars/<hostname>/all.yml — 特定主机变量
# 2. group_vars/<group>/all.yml — 主机组共享变量
# 3. roles/<role>/defaults/main.yml — 角色默认值(可被覆盖)
# 4. Playbook 顶层 vars — Playbook 内联变量
# 5. extra_vars (-e 参数) — 运行时传入,优先级最高
```
> [!tip] 为什么需要多级变量?
> 同一个 `mysql_port` 在开发环境可能是 `3307`,生产环境是 `3306`。**group_vars 按环境隔离 + defaults 提供安全兜底**——这是大型 Ansible 项目中最常用的变量管理模式。
### 变量引用语法
```yaml
# YAML 文件中用双大括号引用
- name: Set MySQL port
debug:
msg: "MySQL is running on port {{ mysql_port }}"
# 在模板文件(.j2)中同样使用
port = {{ mysql_port }}
# 支持字典访问和索引
- name: Access nested variables
debug:
msg: "{{ db_config.host }},{{ db_config.credentials.password }}"
```
### MySQL 部署的角色拆分示例
一个典型的 MySQL 部署 Playbook 会拆成多个 Role:
```
roles/
├── common-setup/
│ ├── tasks/main.yml # 时间同步、内核参数调优、防火墙规则
│ └── templates/ # 系统级配置文件模板
├── install-mysql/
│ ├── tasks/main.yml # 安装、配置主从复制
│ └── files/ # 二进制包或 RPM 安装包
└── config-mysql/
├── tasks/main.yml # 编写 my.cnf(Jinja2 模板)
└── templates/my.cnf.j2 # 根据变量动态生成配置
```
每个 Role 可以独立测试和复用,不同服务的安装只需替换中间两个 Role。
### Jinja2 模板实战
```ini
# my.cnf.j2
[mysqld]
port = {{ mysql_port | default(3306) }}
datadir = {{ mysql_data_dir | default('/var/lib/mysql') }}
server-id = {{ inventory_hostname | regex_replace('\\D', '') | int }}
{% if role == 'master' %}
log-bin = mysql-bin
binlog-format = ROW
{% elif role == 'slave' %}
read-only = 1
relay-log = relay-bin
{% endif %}
```
> [!tip] 为什么需要 Jinja2 模板?
> 同一套 Playbook 要用于 Master 和 Slave,它们的配置有差异。**模板 + 变量注入**比硬编码多个 Playbook 更灵活——这也是 IaC 的核心思想:**一套代码,多种环境**。
## 控制流
### 循环(Loops)
Ansible 推荐使用简洁的 `loop` 语法替代旧式的 `with_items`。`loop` 在 2.5 版本引入,功能等价于 `with_list`,但更简洁且与 `lookup` 兼容更好。
#### 基础用法
```yaml
- name: Install multiple packages
ansible.builtin.yum:
name: "{{ item }}"
state: present
loop:
- vim
- curl
- git
- wget
# 字典列表循环 — 适合批量创建用户/配置
- name: Create database users
community.mysql.mysql_user:
name: "{{ item.name }}"
password: "{{ item.password }}"
priv: "{{ item.priv }}"
state: present
loop: "{{ db_users }}" # 引用 vars 中定义的变量列表
```
#### 字典迭代(dict2items)
当需要遍历 YAML 字典时,用 `dict2items` 将其转为 `[{"key": ..., "value": ...}, ...]` 结构:
```yaml
- name: Configure servers from dictionary
ansible.builtin.debug:
msg: "{{ item.key }}: IP={{ item.value.ip_address }}, role={{ item.value.role }}"
loop: "{{ server_configs | dict2items }}"
vars:
server_configs:
web_01:
ip_address: "10.1.1.50"
role: "frontend"
web_02:
ip_address: "10.1.1.51"
role: "backend"
```
> [!tip] query vs lookup
> `loop` 要求返回一个 list。使用 lookup 插件时,推荐用 `query`(2.5+),它保证返回列表;如果用 `lookup`,需加 `wantlist=True`:
> ```yaml
> loop: "{{ query('inventory_hostnames', 'all') }}"
> # 或
> loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
> ```
#### 循环控制(loop_control)
```yaml
- name: Create cloud instances with delay
digital_ocean:
name: "{{ item.name }}"
state: present
loop:
- { name: server1, disk: 3gb }
- { name: server2, disk: 4gb }
loop_control:
label: "{{ item.name }}" # 自定义输出标识(避免冗长日志)
pause: 3 # 每项之间等待 N 秒(避免瞬时并发压力)
loop_var: outer_item # 指定外层循环变量名(嵌套循环防冲突)
```
#### 自动重试(until 循环)
`until` 不是用来遍历数据的——它是 **轮询重试** 模式,适用于可能暂时失败、最终会成功的操作(如等待服务启动):
```yaml
- name: Wait for service to be ready
shell: /usr/bin/check_service_ready
register: result
until: result.stdout.find("ready") != -1
retries: 5 # 最大尝试次数
delay: 10 # 每次间隔秒数
```
注册变量在循环中的结构是嵌套的:`{{ my_loop.results[0].stdout }}`、`{{ my_loop.results[1].rc }}` 等,后续用 `when: item.rc != 0` 过滤即可。
> [!warning] with_items → loop 迁移注意
> `with_items` 有**隐式扁平化**行为:`with_items: [1, [2,3], 4]` 会展平为 `[1, 2, 3, 4]`。切换到 `loop` 后需手动处理:
> ```yaml
> loop: "{{ [1, [2, 3], 4] | flatten(1) }}"
> ```
### 条件判断(when)
```yaml
- name: Configure slave replication
template:
src: my.cnf.j2
dest: /etc/my.cnf
when: inventory_hostname in groups['mysql_slaves']
# 复杂条件
- name: Only on CentOS 7
debug:
msg: "CentOS detected"
when:
- ansible_os_family == "RedHat"
- ansible_distribution_major_version == "7"
```
> [!warning] 何时不用 when?
> 如果 `when` 只是过滤几行数据,优先在 Jinja2 模板内部用 `{% if %}` 处理,而不是写一个全量任务再套 `when`——后者会在所有目标主机上都执行一次无意义的模块调用。
### notify / handlers 机制详解
```yaml
tasks:
- name: Update Nginx config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx # ← 仅设置"标记",不立即执行
handlers:
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded # ← 仅在 Playbook 阶段结束时触发
```
> [!info] notify 的关键语义
> 即使多个 task 都 `notify` 同一个 handler,handler 也只会**在每个 Play 结束后执行一次**。这正是幂等性设计的一部分——避免重复重启服务。
### 结果捕获与错误处理
```yaml
# register — 将命令输出保存到变量
- name: Check if MySQL data dir exists
ansible.builtin.stat:
path: /data/mysql
register: mysql_data_check
- name: Initialize data dir if missing
ansible.builtin.file:
path: /data/mysql
state: directory
owner: mysql
group: mysql
when: not mysql_data_check.stat.exists
# ignore_errors — 允许任务失败不中断 Playbook
- name: Try to stop already-stopped service
ansible.builtin.service:
name: mysqld
state: stopped
ignore_errors: yes
# failed_when — 自定义失败判定逻辑
- name: Check API health
ansible.builtin.uri:
url: http://localhost:8080/health
register: health_check
failed_when: "'unhealthy' in health_check.content"
# set_fact — 动态设置变量供后续任务使用
- name: Capture private IP
ansible.builtin.set_fact:
node_private_ip: "{{ ansible_default_ipv4.address }}"
```
> [!tip] register 的经典使用场景
> `stat` 模块检查文件存在 → `when` 判断决定是否创建 → 这就是 **"先查后做"** 的 Ansible 范式,完美体现幂等性原则。
### 状态控制(changed_when / any_errors_fatal)
除了 `failed_when` 外,Ansible 还允许通过 **`changed_when`** 精确控制任务的变更状态——这直接影响统计报告和 handler 触发:
```yaml
# ❌ command 永远是 "changed",即使数据库连接无变化
- name: Check DB connection
command: mysql -e "SELECT 1"
# ✅ 仅在真正有变更时标记为 changed
- name: Check DB connection
shell: mysql -e "SELECT 1"
register: db_check
changed_when: "'ERROR' in db_check.stdout"
# 强制不报告变更(常用于健康检查类任务)
- name: Health check endpoint
uri:
url: http://localhost:8080/health
changed_when: false
```
| 参数 | 作用域 | 说明 |
|------|--------|------|
| **ignore_errors** | Task 级 | 失败继续执行,不中断 Play |
| **ignore_unreachable** | Play 级 | 跳过网络不可达的主机,继续其他主机 |
| **any_errors_fatal** | Play/Block 级 | 任一主机报错即终止全部执行 |
| **max_fail_percentage** | Play 级 | 失败超过阈值(%)时停止 |
| **changed_when** | Task 级 | 条件性覆盖 "已变更" 报告 |
| **failed_when** | Task 级 | 条件性覆盖 "失败" 判定 |
### Block / Rescue / Always — 结构化异常处理
这是编程中 `try/catch/finally` 的 Ansible 等价物,适合需要 **原子化操作 + 自动回滚** 的场景:
```yaml
- name: Deploy application with rollback
block:
- name: Backup current release
archive:
path: /opt/app/current
dest: /opt/backups/release-$(date +%s).tar.gz
register: backup_result
- name: Extract new version
unarchive:
src: new-app-v2.tar.gz
dest: /opt/app/releases/v2
remote_src: yes
- name: Symlink to active release
file:
src: /opt/app/releases/v2
dest: /opt/app/current
state: link
rescue:
- name: Log failure details
debug:
msg: "Task '{{ ansible_failed_task.name }}' failed on {{ inventory_hostname }}"
- name: Rollback to previous release
file:
src: /opt/app/releases/v1
dest: /opt/app/current
state: link
when: backup_result is succeeded # 仅当备份成功时才尝试还原
always:
- name: Notify deployment team
ansible.builtin.command: >
curl -X POST https://hooks.slack.com/deploy-webhook
-d '{"text":"Deployment {{ ansible_failed_task.name if ansible_failed_task else 'succeeded' }} on {{ inventory_hostname}}"}'
changed_when: false
```
> [!info] 语义对比表
>
> | 编程概念 | Ansible 关键字 | 触发条件 |
> |----------|---------------|---------|
> | `try {}` | `block:` | 正常任务流,始终执行 |
> | `catch {}` | `rescue:` | 仅当前置任务返回 `failed` 时触发 |
> | `finally {}` | `always:` | 无论成败均执行 |
>
> - **Rescue 中的恢复若成功,Play 会继续**——但统计日志仍记录一次故障
> - **语法错误、断连等情况不会触发 Rescue**,与编程语言不同
> - **Handler 可以在 Rescue 中被调用**(通过元数据命令触发)
## 常用模块速查
> [!info] 命名空间规范(Namespace Prefix)
> Ansible 2.10+ 开始引入 `collections` 概念,推荐始终使用前缀格式:
> - `ansible.builtin.copy` — 内置模块
> - `community.general.selinux` — 社区集合
> - 现代 Playbook 中优先写全称;如果省略前缀,Ansible 会按内置 → 已安装的集合顺序自动匹配。
| 模块 | 用途 | 关键参数 |
|------|------|---------|
| **ansible.builtin.copy** | 本地文件复制到远程 | `src`, `dest`, `owner`, `mode` |
| **ansible.builtin.template** | Jinja2 模板渲染后复制 | `src (.j2)`, `dest`, `owner`, `mode` |
| **ansible.builtin.file** | 管理文件/目录权限和属性 | `state: touch/file/directory/link/absent`, `mode` |
| **ansible.builtin.yum** / **ansible.builtin.apt** | 包管理 | `name`, `state: present/installed/latest/absent` |
| **ansible.builtin.service** | 管理服务生命周期 | `name`, `state: started/stopped/restarted/reloaded` |
| **ansible.builtin.lineinfile** | 确保某行存在或不存在于文件中 | `path`, `line`, `state`, `regexp` |
| **ansible.builtin.replace** | 正则替换文件内容 | `path`, `regexp`, `replace` |
| **ansible.builtin.command** / **ansible.builtin.shell** | 执行命令(慎用) | `cmd`, `chdir` |
| **ansible.builtin.get_url** / **ansible.builtin.uri** | HTTP 下载 / API 调用 | `url`, `dest`, `method`, `status_code` |
| **ansible.builtin.user** | 管理系统用户 | `name`, `state`, `groups`, `shell` |
| **ansible.builtin.cron** | 管理 crontab | `name`, `minute`, `job`, `state` |
| **ansible.builtin.debug** | 调试输出 | `msg`, `var` |
| **ansible.builtin.stat** | 检查文件/路径属性 | `path`, returns `.stat.exists` |
| **community.mysql.mysql_user** | MySQL 用户管理 | `name`, `password`, `priv`, `state` |
| **community.postgresql.postgresql_user** | PostgreSQL 用户管理 | `name`, `password`, `role_attr` |
> [!warning] command vs shell 的选择
> `command` 不经过 Shell 解释器(更安全,推荐优先使用),`shell` 才支持管道 `\|`、重定向 `>`、变量扩展 `$VAR`。**能用模块完成的操作绝不碰 command/shell**。
## 常见陷阱与最佳实践
### 1. 不要跳过 `state` 检查
错误写法:
```yaml
- ansible.builtin.command: systemctl start mysqld # ❌ 每次都会尝试启动,即使已在运行
```
正确写法:
```yaml
- ansible.builtin.service: # ✅ Ansible 自带的 service 模块会检查当前状态
name: mysqld
state: started
```
### 2. Inventory 分层管理
把开发、测试、生产环境的机器分开管理:
```ini
# inventory/prod
[mysql_prod]
mysql-prod-01 ansible_host=10.0.1.11
mysql-prod-02 ansible_host=10.0.1.12
[redis_prod]
redis-prod-01 ansible_host=10.0.1.21
```
### 3. 首次运行时务必先 Dry Run
```bash
# --check 只做预演,不实际执行
ansible-playbook deploy.yml --check --diff
```
### 4. 警惕 shell/command 失去幂等性
```yaml
# ❌ 每次运行都会在日志末尾追加一行
- ansible.builtin.shell: echo "deployed" >> /tmp/deploy-history
# ✅ 用 file module 保证状态一致性
- ansible.builtin.file:
path: /tmp/deploy-marker
state: touched
modification_time: preserve
```
## 延伸阅读
- [[technical/xinfra-preview/k8s-rke2-fundamentals]] — K8s 内的实例 vs Ansible 管理的裸机/VM
- [[technical/xinfra-preview/cachecloud-overview]] — CacheCloud 管 Redis,Ansible 负责底层部署
- [[technical/xinfra-preview/cloud-dm-overview]] — Ansible 部署的 MySQL 由 CloudDM 做 SQL 审核
- [官方 Playbook 指南](https://github.com/ansible/ansible-documentation/tree/devel/docs/docsite/rst/playbook_guide) — Ansible 文档源码,涵盖从入门到高级语法的全部章节
- https://www.ansible.com/docs — Ansible 官方文档入口
- [Ansible Vault 文档](https://docs.ansible.com/ansible/latest/cli/ansible-vault.html) — 加密敏感凭据(密码、密钥),解决 Become 中的安全合规问题
- [入门教程](https://www.cnblogs.com/easonscx/p/10622781.html) — Ansible 基础用法参考
@@ -0,0 +1,377 @@
---
tags: [cachecloud, redis, infra, self-hosted]
create time: 2026-07-04 12:00
---
# CacheCloud Redis 管理台
## 概述
CacheCloud(搜狐开源)是一个 **Redis 私有云平台**,支持 Standalone、Sentinel、Cluster 三种架构的一站式高效管理。xinfra 中所有需要 Redis 服务的场景都通过 CacheCloud 进行**统一的实例申请和管理**——开发同学不再需要手动安装部署 Redis,只需在平台上提交工单即可获取可用实例。
核心目标:**降低大规模 Redis 运维成本,提升资源管控能力和利用率**。平台提供快速搭建/迁移、运维管理、弹性伸缩、统计监控、客户端整合接入等功能。
---
## 系统架构
### 整体拓扑
```mermaid
graph TB
subgraph "用户层"
Dev[开发同学 / 运维人员]
end
subgraph "接入层 (Nginx 双机房)"
NG1[Nginx 机房 A]
NG2[Nginx 机房 B]
VIP[Virtual IP → 双向漂移]
VIP --> NG1
VIP --> NG2
end
subgraph "应用层 (Spring Boot)"
CC[CacheCloud Web Server]
DB[(MySQL 元数据)]
AGT_Mgr[Agent 管理器]
ALERT[报警组件<br/>邮件 / 微信 / HTTP]
CUSTOM[自定义扩展模块]
end
subgraph "执行层 (Agent 代理)"
AGT1[Agent 宿主机 A<br/>Redis Standalone / Sentinel]
AGT2[Agent 宿主机 B<br/>Redis Cluster Shard 1]
AGT3[Agent 宿主机 C<br/>Redis Cluster Shard 2]
end
Dev -->|cc.company.com| VIP
VIP --> NG1 & NG2
NG1 & NG2 --> CC
CC <--> DB
CC --> AGT_Mgr
CC --> ALERT
CC --> CUSTOM
AGT_Mgr -->|SSH + 心跳| AGT1 & AGT2 & AGT3
```
> **关键设计决策**:CacheCloud 采用 **Agent 模式**而非直接 SSH 连接 Redis 实例。每个宿主机部署一个 Agent 进程,负责该机器上所有 Redis 实例的生命周期管理(启停、升级、备份恢复)。这样做的好处是 Agent 可复用 SSH 连接、缓存本地状态,大幅减少管理平台到 Redis 主机的网络开销。
### 核心组件
| 组件 | 职责 |
|------|------|
| **Web UI / REST API** | 自助申请实例、配置修改、工单审批、监控大屏 |
| **Agent 代理** | 部署在每个宿主机上,负责 Redis 进程生命周期管理、状态上报、远程命令执行 |
| **元数据库 (MySQL)** | 存储实例拓扑、配置模板、账号权限、工单流转记录 |
| **报警模块** | 内置邮件和微信报警,同时暴露 HTTP 接口供任意语言集成 |
| **扩展模块** | 登录组件 (`LoginComponent`)、报警组件 (`EmailComponent`/`WeChatComponent`) 均可自行实现 |
---
## 实例类型与选型指南
### 三种架构对比
| 维度 | Standalone | Sentinel (Master-Slave) | Cluster |
|------|-----------|------------------------|---------|
| **高可用** | ❌ 单机,无 Failover | ✅ 主从 + Sentinel 自动故障转移 | ✅ 多分片,自动槽迁移 |
| **横向扩展** | ❌ 受单机容量限制 | ❌ 受单机容量限制 | ✅ 新增节点即扩容 |
| **复杂度** | 简单 | 中等 | 较高 |
| **推荐场景** | 测试环境、低频读写缓存 | 生产环境常规业务、内存需求 ≤ 6GB | 大数据量、高并发、内存需求 > 6GB |
> **建议**:并不是 Cluster 越好。如果当前或未来所需内存不超过 6GB 且要求高可用,选择 Sentinel 就足够了。
### 选型决策树
```mermaid
flowchart TD
Start{是否需要高可用?}
Start -->|否: 测试/灰度| Standalone[Standalone]
Start -->|是| NeedScale{是否需要横向扩展?}
NeedScale -->|否≤6GB| Sentinel[Sentinel 主从]
NeedScale -->|是>6GB| Cluster[Redis Cluster]
```
---
## 客户端接入方式
CacheCloud 提供了多种客户端 SDK 及 REST API,覆盖 Java、Python 等主流语言。
### REST API(最通用)
通过简单 HTTP 请求即可获取实例连接信息,适用于任何语言:
```
GET http://{domain}/cache/client/redis/{appType}/{appId}.json?clientVersion={version}
```
**参数说明**:
| 参数 | 含义 | 枚举值 |
|------|------|--------|
| `appType` | 实例类型 | `cluster` / `sentinel` / `standalone` |
| `appId` | 应用 ID | 平台分配的数字 ID |
**响应示例**:
```json
{
"message": "client is up to date, Cheers!",
"shardNum": 10,
"appId": 10192,
"status": 1,
"shardInfo": "10.10.xx.xx:6390,10.10.xx.xx:6382 10.10.xx.xx:6387,10.10.xx.xx:6379 ..."
}
```
> `shardInfo` 字段以空格分隔每个分片,逗号分隔同一个分片的主从地址。解析后即可直连。
### Java — cachecloud-client-redis(Jedis 封装)
```java
@Configuration
public class RedisConfiguration {
@Bean(destroyMethod = "close")
public PipelineCluster pipelineCluster(@Value("${cachecloud.demo.appId}") long appId) {
return ClientBuilder.redisCluster(appId).build();
}
@Bean(destroyMethod = "destroy")
public JedisSentinelPool jedisSentinelPool(@Value("${cachecloud.demo.appId}") long appId) {
return ClientBuilder.redisSentinel(appId).build();
}
@Bean(destroyMethod = "destroy")
public JedisPool jedisPool(@Value("${cachecloud.demo.appId}") long appId) {
return ClientBuilder.redisStandalone(appId).build();
}
}
```
**使用方式**:
```java
@Autowired private PipelineCluster pipelineCluster;
public String get(String key) {
return pipelineCluster.get(key);
}
```
> 所有 `...Pool.getResource()` 使用后必须调用 `jedis.close()` —— 它并非真正关闭连接,而是将连接归还给连接池(内部判断连接是否损坏后决定 `returnResource` 还是 `returnBrokenResource`)。
### Java — cachecloud-client-lettuce(Lettuce 封装)
适合需要异步/响应式场景的应用:
```java
@Bean(destroyMethod = "shutdown")
public RedisClusterClient redisClusterClient(long appId, String password) {
return LettuceClientBuilder.redisCluster(appId, password).build();
}
@Bean(destroyMethod = "close")
public StatefulRedisClusterConnection<String, String> clusterConnection(RedisClusterClient client) {
StatefulRedisClusterConnection<String, String> conn = client.connect();
conn.setReadFrom(ReadFrom.REPLICA_PREFERRED); // 读操作优先走从节点
return conn;
}
```
### Python 接入(REST API 动态拉取连接信息)
```python
import requests
from rediscluster import RedisCluster
app_id = 10192
url = f'http://cc.company.com/cache/client/redis/cluster/{app_id}.json'
resp = requests.get(url).json()
startup_nodes = [
dict(zip(['host', 'port'], addr.split(':')))
for shard in resp['shardInfo'].split(' ')
for addr in shard.split(',')
]
rc = RedisCluster(startup_nodes=startup_nodes, password='your-pass')
```
### 跨机房部署(Cross-Room)
对于容灾要求高的业务,CacheCloud 支持**跨机房双活**:同一业务在两个机房分别部署应用实例,客户端 SDK 自动做双写双读和机房切换。原理是两个 `PipelineCluster` 实例被包装进一个 `RedisCrossRoomClient`。
---
## 系统功能全景
### 用户端功能
| 功能 | 说明 |
|------|------|
| **应用管理** | 查看统计信息、实例列表、应用拓扑、连接信息 |
| **监控面板** | 命令曲线、延迟监控、日报统计 |
| **命令执行** | 在线执行 Redis 命令用于排查 |
| **键值分析** | 分析 bigkey、hotkey 分布 |
### 运维端功能
| 功能分类 | 具体能力 |
|----------|---------|
| **数据统计** | 全局统计、client 统计、server 统计 |
| **工单审批** | 实例申请、配置修改、数据清理等操作需管理员审批 |
| **应用运维** | 应用维度的启停、配置下发、日志查看 |
| **实例运维** | 实例维度的启停、配置查询、数据清理 |
| **数据迁移** | 跨实例数据迁移工具 |
| **诊断工具** | 慢查询分析、连接数诊断等 |
| **模板管理** | 按规格预设 Redis 配置模板(maxmemory、持久化策略等) |
| **任务流** | 编排运维操作流程(如批量升级) |
### 报警配置
CacheCloud 内置的报警覆盖了 Redis 机器级别和实例级别的重要指标,支持的报警渠道:
- **邮件报警** — 默认实现,也可替换为 HTTP 回调
- **微信报警** — 企业微信/钉钉 webhook 风格
报警可通过 HTTP 接口自定义,格式如下:
```
POST www.xxx.com/emailAlert?title=xx&content=xx&receiver=x&cc=x
POST www.xxx.com/weChatAlert?title=xx&message=xx&weChatList=xx
```
---
## 基础运维实践
### maxmemory-policy 策略选择
| 策略 | 行为 | 适用场景 |
|------|------|---------|
| `volatile-lru` | 删除有过期时间的 key,LRU 淘汰 | 默认策略,有 TTL 的缓存 |
| `allkeys-lru` | 对所有 key 做 LRU 淘汰 | 纯缓存场景,不设 TTL |
| `volatile-ttl` | 删除即将过期的 key | 希望按过期时间优先淘汰 |
| `noeviction` | 不淘汰,写操作返回错误 | 需要绝对保证数据完整性 |
> **注意**:修改的配置会对应用的所有节点生效,因为所有节点的配置是统一的。
### Jedis 连接池调优参考
```java
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(DEFAULT_MAX_TOTAL * 5); // 根据实际 QPS 调整
poolConfig.setMaxIdle(DEFAULT_MAX_IDLE * 3);
poolConfig.setMinIdle(DEFAULT_MIN_IDLE * 2);
poolConfig.setMaxWaitMillis(3000); // 连接耗尽时最多等待 3s
poolConfig.setJmxEnabled(true); // 开启 JMX 便于观察
poolConfig.setTestWhileIdle(true); // 空闲时定期检查连接有效性
poolConfig.setTimeBetweenEvictionRunsMillis(60000); // 每分钟检查一次
```
关键字段解读:
| 配置项 | 默认值 | 调优建议 |
|--------|--------|---------|
| `maxTotal` | 8 | 根据峰值连接数估算,通常 × 3~5 |
| `testOnBorrow` | false | 不建议设为 true,会显著增加延迟 |
| `testOnReturn` | false | 一般保持 false,由 `testWhileIdle` 兜底 |
| `whenExhaustedAction` | 1 (阻塞) | 配合 `maxWaitMillis` 使用,避免无限等待 |
---
## 常见陷阱与最佳实践
### 1. Bigkey 的寻找与优化
**什么是 bigkey**:value 所占内存空间较大的 key。字符串类型超过 100KB 即视为 bigkey;非字符串类型(Hash/List/Set/ZSet)则以元素数量过多为准。
**危害**:
- 内存不均匀:Cluster 中造成部分节点内存暴增
- 超时阻塞:Redis 单线程特性下,大 key 操作耗时 > 客户端超时
- 网络拥塞:单次大流量冲击网卡,影响同机其他实例
- 过期删除阻塞:未启用 lazyfree 时阻塞主线程
- 碎片整理冲突:Redis 4.0+ activeDefrag 对超大 key 可能导致周期性延迟
**发现手段**:
| 方法 | 命令 | 特点 |
|------|------|------|
| redis-cli | `redis-cli --bigkeys` | 全量扫描,建议在从节点执行 |
| DEBUG OBJECT | `DEBUG OBJECT key` | 获知序列化长度 `serializedlength` |
| MEMORY USAGE | `MEMORY USAGE key` | 仅 Redis 4.0+,返回精确内存占用 |
| 监控输出缓冲区 | `info clients` 关注 `client_biggest_input_buf` / `client_recent_max_output_buffer` | 间接判断是否存在大 key 读取 |
**优雅删除方案**:
- **String**:直接使用 `DEL`,通常不会阻塞
- **Hash/List/Set/ZSet**:使用 `HSCAN/SSCAN/ZSCAN` 分批获取元素 + `HDEL/SREM/ZREM` 逐个删除,或使用 Redis 4.0+ 的 `UNLINK`(异步删除)
```python
def del_big_hash(r, key):
cursor = 0
while True:
cursor, members = r.hscan(key, cursor, count=100)
if not members:
break
r.hdel(key, *members) # pipeline 批量更高效
if cursor == 0:
break
```
### 2. Hotkey 的处理方向
当某个 key 的访问量远超平均水平时:
- **本地缓存**:在应用侧加一层 Guava/Caffeine 缓存
- **key 拆分**:将一个大 key 拆成多个子 key(如 `user:1001:friends` → `user:1001:friends:1`, `user:1001:friends:2`)
- **读写分离**:读操作通过 Sentinel 路由到从节点
### 3. 实例规格匹配
不要盲目选大规格。建议先通过 Prometheus/Grafana 观察历史 QPS、内存使用和连接数,再确定实例规格。过度配置会在资源看板上体现为浪费。
### 4. 临时 vs 长期实例
- **临时实例**(测试/灰度用)应设置自动回收策略——超出保留期后系统自动销毁
- **长期实例**则需要完善监控、定期巡检配置变更
### 5. 机房就近原则
Redis 对网络延迟非常敏感。跨机房访问比同机房慢数倍,因此应用申请时应填写服务所在机房,确保实例分配到最近的机器。
---
## Redis 版本演进速览
CacheCloud 管理的 Redis 实例可能运行在不同版本,了解各版本的关键特性有助于理解平台行为差异:
| 版本 | 核心新特性 | CacheCloud 相关 |
|------|-----------|----------------|
| **3.x** | 原生 Cluster | 最早支持的 Cluster 形态 |
| **4.0** | Lazyfree 异步删除、AOF/RDB 混合持久化、内存碎片整理 | 支持 UNLINK 删除 bigkey |
| **5.0** | Stream 数据类型、RESP3 协议、Dynamic HZ | 客户端兼容性更好 |
| **6.0** | 多线程 IO、ACL、SSL 加密、协助客户端缓存 | 多线程 IO 提升非 Pipeline 场景 2 倍性能 |
> **启发问题**:为什么 Redis 6.0 的多线程只处理网络 IO 而不涉及命令执行?这背后有什么权衡?
---
## 与 xinfra 的对接方向
进阶目标中提到要「基于 CacheCloud API 实现页面直接创建 Redis Cluster」,这意味着:
1. **阅读 Open API 文档**:梳理可用的创建/配置/查询接口
2. **封装创建流程**:选择规格 → 调用 API → 等待就绪 → 返回连接信息
3. **集成到 xinfra 统一服务管理页面**:让开发者在 xinfra 内即可完成 Redis 实例的全生命周期操作
4. **可选增强**:对接 xinfra 的审批流、用量统计、成本分摊等模块
---
## 关联笔记
- [[technical/xinfra-preview/cloud-dm-overview]] — SQL 审核与 Redis 访问同属数据层基础设施
- [[technical/xinfra-preview/k8s-rke2-fundamentals]] — CacheCloud 的宿主机未来可能迁移至 K8s 部署
- https://github.com/sohutv/cachecloud — CacheCloud GitHub 仓库(含完整 Wiki 和代码)
- https://github.com/antirez/redis — Redis 官方仓库
@@ -0,0 +1,130 @@
---
tags: [cloud-dm, sql-audit, dbms, preview, infra]
create time: 2026-07-04 12:00
---
# CloudDM SQL 审核平台
## 概述
CloudDM(open-cdm)是一款**免费且开源的团队化数据库管理平台**,由 ClouGence 团队开发,采用 Apache License 2.0 许可。它的定位远超"SQL 审核工具"——覆盖数据查询、权限管控、SQL 审核、数据脱敏、数据库 CI/CD 和跨地区部署的全链路能力。
在 xinfra 中,所有操作 MySQL 的场景都必须经过 CloudDM 的审核机制——这是防止**误操作直接打到生产库**的第一道防线。同时,开发人员也可以直接在 CloudDM 中进行安全的数据查询与变更交付。
当前版本:**3.1.1** | 官网:<https://www.cdmgr.com/>
## 核心概念
### 定位与设计哲学
```mermaid
sequenceDiagram
participant dev as 开发人员
participant cdm as CloudDM
participant auditor as DBA/审核人
participant mysql as MySQL实例
dev->>cdm: 提交SQL上线工单 / 数据查询
cdm->>cdm: SQL语法检查 + 54条规则校验
cdm->>auditor: 通知审核
auditor->>cdm: 审核通过 / 驳回
cdm->>mysql: 按窗口执行(限流+分批)
```
核心原则:**每一次数据库操作都要有记录、可审计、可回滚**。
### 核心能力模块
| 模块 | 职责 |
|------|------|
| **数据查询** | 支持 MySQL、Oracle、PG、DB2、SQL Server、OceanBase、ClickHouse、Redis、MongoDB 等 **20+** 数据源;提供统一 Web 控制台,含语法高亮、智能提示、执行计划预览 |
| **SQL 审核** | 内置 54 条审核规则,支持规则脚本自定义扩展;执行前预检,提示风险或阻断高危语句 |
| **权限控制** | 资源权限(实例/库/Schema/表粒度)+ 功能权限(RBAC 角色授权);支持申请权限、赋予权限及临时权限 |
| **数据管理** | 可视化管理数据库对象:库、表、索引、视图、函数、存储过程、用户、角色等 |
| **数据脱敏** | 对查询结果或流程中的敏感数据进行隐藏或转换 |
| **数据库 CI/CD** | Git Push / Web Hook / HttpCall 三种触发方式,支持 Gitee 作为变更仓库 |
| **协同流程** | 三种流程类型:**SQL 审核**、**权限工单**、**变更流程**;三种执行方式:**手动执行**、**立即执行**、**定时执行** |
| **统一认证** | OpenLDAP / OIDC / Windows AD / 钉钉 / 飞书 / 企业微信 SSO |
### 部署模式对比
| 维度 | Alone(单机) | Console + Sidecar(集群) |
|------|---------------|---------------------------|
| 组件 | Web + Sidecar + MySQL 合一 | Console 独立 + 多个 Sidecar |
| 适用场景 | 小规模验证、个人使用 | 团队协作、跨地区多数据源 |
| 特点 | 开箱即用,一条 Docker 命令启动 | 最大特点是**跨地区数据库统一授权访问** |
| Web 端口 | 8222 | Console: 8222, Sidecar: 8080 |
> [!tip] 快速体验
> ```bash
> # 一键启动单机版
> docker run -d --name cgdm-alone -p 8222:8222 bladepipe/cgdm-alone:3.1.1
> # 中国区加速镜像
> docker run -d --name cgdm-alone -p 8222:8222 \
> cloudcanal-registry.cn-shanghai.cr.aliyuncs.com/clougence/cgdm-alone:3.1.1
> ```
### 典型操作流程
```
1. 开发在 CloudDM 添加数据源(MySQL / PG / Redis ...)
2. 通过 Web 查询编辑器进行数据查询或编写变更 SQL
3. 若涉及变更 → 创建 SQL 上线工单
4. CloudDM 自动执行 54 条规则预检
5. DBA 审核通过后进入排队队列
6. 按指定时间窗或立即由系统执行
7. 执行结果回填工单,全程留痕
```
### 元数据存储
CloudDM 的元数据存储在内置(Alone 模式)或外部 MySQL 中。无需深入具体表结构——Console 提供了完整的可视化对象管理界面。需要了解的重点是:
- 元信息数据库使用 **MariaDB/MySQL**(Alone 模式自带嵌入式 MySQL)
- 集群模式的元数据持久化为 `cdmgr` 库,默认账号 root
- 忘记管理员密码时,可删除 `drivers/cgdm-runtime-mysql` 目录后重启以重新走初始化向导(不重建数据库即可重置)
## 常见陷阱与最佳实践
### 1. 审核规则要适配实际业务
过于严格的规则可能在特定场景下误杀正常需求。**建议**:初期允许白名单豁免,积累一段时间后再收紧规则。
### 2. 区分 DML 和 DDL 审核策略
- **DML**(INSERT/UPDATE/DELETE):重点防范误删、锁表
- **DDL**(ALTER TABLE/CREATE INDEX):重点防范大表长时间锁表
两者需要不同的审核标准和执行窗口。
### 3. Alone vs Cluster 选型
- 单人或小团队直接用 **Alone 模式**,一条 `docker run` 即可上手
- 当需要在不同地域连接多台数据库并做统一权限管控时,迁移到 **Console + Sidecar 集群模式**
### 4. 生产部署安全事项
- 务必替换默认的 JWT 密钥和管理员密码
- K8s 部署中将敏感配置改为 `Secret` 管理
- PVC 容量根据实际数据规模调整
## 关键术语速查
| 术语 | 含义 |
|------|------|
| **Alone** | 单机部署模式,Web 控制台、Sidecar 和元信息数据库合并运行 |
| **Console** | 中央 Web 控制台,负责数据库访问、审批流程和全局配置 |
| **Sidecar** | 配合 Console 使用的数据库访问代理,常用于跨地区部署 |
| **SQL 审核** | SQL 执行或变更交付前的规则化风险检查 |
| **数据脱敏** | 查询结果或流程中对敏感数据的隐藏/转换保护 |
| **资源权限** | 按实例、库、Schema、表粒度授予的权限 |
| **功能权限** | 基于 RBAC 的功能访问权限 |
| **权限工单** | 用于申请、审批和授予访问权限的流程 |
| **变更流程** | 用于审核和交付受控数据库变更的流程 |
| **数据库 CI/CD** | 通过 Git Push / Web Hook / HttpCall 触发的数据库变更交付 |
## 延伸阅读
- [[technical/xinfra-preview/k8s-rke2-fundamentals]] — K8s 内的 MySQL 容器化部署基础
- <https://github.com/ClouGence/open-cdm> — open-cdm 主仓库(GitHub)
- <https://gitee.com/clougence/open-cdm> — 中国镜像仓库
- [[technical/xinfra-preview/cloud-dm-overview#核心能力模块]] — 本文档「核心能力模块」章节
@@ -0,0 +1,375 @@
---
tags: [k8s, rke2, preview, infra]
create time: 2026-07-04 12:00
---
# K8s + RKE2 基础
## 概述
Kubernetes(K8s)是容器编排的事实标准,Rancher RKE2 是 CNCF 认证的轻量级、安全优先的 K8s 发行版,专为边缘和混合云场景设计。xinfra 平台的容器调度底层基于 RKE2,后续所有服务部署都建立在对这个知识栈的理解之上。
## 核心概念
### Kubernetes 核心对象关系
```mermaid
graph TD
Node[Node / 节点] --> Pod[Pod / 最小部署单元]
Pod --> C[Containers / 容器]
Deployment[Deployment / 副本控制] --> Pod
Service[Service / 网络暴露] --> Pod
Namespace[Namespace / 资源隔离] --> Deployment
Namespace --> Service
PV[PersistentVolume / 持久存储] --> PVC[PVC / 申请存储]
PvcRef[PVC ref by Pod] --> Pod
```
| 对象 | 职责 | 类比 |
|------|------|------|
| **Node** | 运行 Pod 的物理机或虚拟机 | 服务器 |
| **Pod** | 一个或多个容器的打包体 | 应用实例 |
| **Deployment** | 管理 Pod 的副本数、滚动更新策略 | 发布控制器 |
| **Service** | 为一组 Pod 提供稳定的访问入口(负载均衡) | 反向代理 |
| **Namespace** | 逻辑隔离的资源分组 | 多租户文件夹 |
| **ConfigMap / Secret** | 将配置注入 Pod(非敏感/敏感数据分离) | .env 文件 |
| **Ingress** | 七层路由,HTTP/HTTPS 域名到 Service 的映射 | Nginx 规则 |
### RKE2 vs 原生 K8s
RKE2(Rancher Kubernetes Engine v2)的核心特点:
1. **单二进制部署**:不需要单独安装 etcd、containerd 等依赖,一条命令拉起整集群
2. **SST(Simple System Tray)**内置 SQLite,etcd 作为可选而非必须
3. **FIPS 140-2 合规**:内置加密要求,适合企业对安全审计的需要
4. **自动注册与拉取**:可配合 Rancher Server 实现节点无感加入
> [!info] 为什么选 RKE2?
> xinfra 需要管理**七机房多套集群**,RKE2 的"一键部署 + 低运维成本"特性正好契合——减少环境差异导致的兼容问题,让平台专注于上层抽象而非底层排障。
### RKE2 安装方式对比
| 方式 | 命令特点 | 适用场景 | 是否支持 Rancher 集成 |
|------|---------|---------|---------------------|
| **单二进制直接运行** | `curl -sfL https://get.rke2.io | sh -` | 开发测试、小规模集群(≤3 节点) | ✅ — 自动发现并注册 |
| **Systemd 服务管理** | 安装后通过 `systemctl enable --now rke2-server` | 生产环境标准化部署 | ✅ |
| **Air-gap(离线)** | 预下载 `.tar.gz` 包 + `INSTALL_DIR=/opt/rke2` 指向本地文件 | 内网无外网环境的机房 | ❌ — 需手动同步镜像 |
> [!warning] Air-gap 是常见坑点
> RKE2 Server/Agent 首次启动时会从官方 registry 拉取 K8s 组件 OCI 镜像。如果机房**无法访问外网**,必须提前准备 image bundle tarball,并通过 `--image-volume-mount /var/lib/rancher/rke2/agent/image-store` 指定本地缓存路径。
### 节点注册机制详解
Worker 节点加入集群有两种认证方式:
```bash
# 方式 1:静态 Token(最简单,适合小集群)
# 服务端会在 /etc/rancher/rke2/rke2.yaml 旁生成 token
export TOKEN="K10xxx...from server node"
rke2 agent --server https://master-ip:6443 --token $TOKEN
# 方式 2:Bootstrap 证书签名(推荐,支持 TLS 双向认证)
# Agent 首次连接时发送 CSR(Certificate Signing Request),
# kube-controller-manager 审批后颁发客户端证书
```
关键文件分布:
| 路径 | 内容 | 说明 |
|------|------|------|
| `/etc/rancher/rke2/rke2.yaml` | kubeconfig(含 admin 权限证书) | ⚠️ 等同于集群 master key,务必备份保护 |
| `/var/lib/rancher/rke2/agent/` | 容器镜像、kubelet 证书 | Agent 工作目录 |
| `/var/lib/rancher/rke2/server/` | etcd 数据、控制平面组件 | Server 工作目录 |
| `/var/lib/rancher/rke2/bin/` | rke2、kubectl、crictl 等 | 可执行文件软链到原位置 |
### RKE2 配置管理
RKE2 支持多种配置来源,优先级从低到高:
```yaml
# 1. 系统级配置(最低优先级)
# /etc/rancher/rke2/config.yaml
tls-san:
- "k8s.company.com"
cluster-cidr: 10.42.0.0/16
service-cidr: 10.43.0.0/16
# 2. Systemd Override(覆盖默认 service 参数)
# /etc/systemd/system/rke2-server.service.d/override.conf
[Service]
Environment="RKE2_TOKEN=${TOKEN}"
Environment="NODE_LABEL=node-role=true,datacenter=bj"
# 3. CLI 参数(最高优先级,覆盖 config.yaml)
# systemctl start rke2-server --token=TOKEN --cluster-cidr=10.42.0.0/16
```
> [!tip] 自定义 CNI 插件
> RKE2 默认使用 Canal(Calico + Flannel 组合)。如果需要更换为 Cilium,只需在 `config.yaml` 中添加:
> ```yaml
> cni: cilium
> ```
> 重新拉起 Server 即可自动替换网络方案——这是 RKE2 比原生 K8s 更省心的地方:**网络方案可以配置驱动,而不是代码依赖**。
### RKE2 集群架构
```mermaid
graph TB
subgraph "Master Node(控制平面)"
RKE2[RKE2 Server<br/>rke2 server --token=TOKEN]
APIServer[Kube-apiserver]
ControllerMgr[Kube-controller-manager]
Scheduler[Kube-scheduler]
ETCD[(etcd DB)]
CCM[Cloud Controller Manager]
APIServer --> ControllerMgr
APIServer --> Scheduler
APIServer <--> ETCD
RKE2 --> APIServer
RKE2 --> ControllerMgr
RKE2 --> Scheduler
RKE2 --> ETCD
RKE2 --> CCM
end
subgraph "Worker Node(工作节点)"
RKE2W[RKE2 Agent<br/>rke2 agent --server=https://MASTER_IP:6443 --token=TOKEN]
Kubelet1[Kubelet]
KubeProxy1[Kube-proxy]
Containerd1[containerd]
RKE2W --> Kubelet1
RKE2W --> KubeProxy1
RKE2W --> Containerd1
end
Dev[kubectl / Wayne API] --> APIServer
APIServer -. kubeconfig .-> RKE2W
Kubelet1 -. etcd sync .-> ETCD
```
关键流程说明:
1. **RKE2 Server 启动后**会自动拉取对应 K8s 版本的组件包(kube-apiserver、controller-manager 等),解压到 `/var/lib/rancher/rke2/`,通过 systemd 管理运行
2. **Agent 节点**只需传入 `--token`(静态认证令牌)和 `--server` URL,即可自动完成 TLS 证书握手并加入集群
3. **默认使用 containerd** 作为容器运行时——无需单独安装 Docker,这也是 RKE2 比 k3s 更"正规"的原因之一
### Pod 生命周期与调度流程
Pod 从创建到运行的完整链路:
```mermaid
sequenceDiagram
participant user as 开发者 (kubectl/Wayne)
participant apiserver as kube-apiserver
participant scheduler as kube-scheduler
participant node as Node Kubelet
participant containerd as containerd
user->>apiserver: POST /api/v1/namespaces/default/pods
apiserver->>apiserver: 验证 RBAC + 持久化到 etcd
Note over apiserver: Pod 状态 = Pending
apiserver-->>user: 201 Created
scheduler->>apiserver: LIST pods (unassigned)
scheduler->>scheduler: 预选 (Fit) → 优先级排序 (Score)
scheduler->>apiserver: PATCH pod -> set nodeName
Note over apiserver: Pod 状态仍为 Pending(等待 Kubelet 响应)
apiserver-->>node: WATCH pod assigned to this node
node->>containerd: Pull image (if not cached)
node->>containerd: Create container
node->>node: Set readinessProbe
Note over node: Pod 状态 = Running
node->>apiserver: UPDATE pod status
```
> **启发问题**:为什么调度器选中节点后,Pod 不会立即变成 Running,而还需要 Kubelet 配合?——因为调度器只负责"选地址",真正的镜像拉取、容器创建、网络挂载都由 Kubelet 在本机完成。这体现了**控制面与数据面的职责分离**。
### 典型部署流程
```bash
# 1. 编写 Deployment YAML
kubectl apply -f deployment.yaml
# 2. 编写 Service YAML
kubectl apply -f service.yaml
# 3. 查看状态
kubectl get pods -n <namespace>
kubectl describe pod <pod-name> -n <namespace>
# 4. 滚动更新(改镜像版本)
kubectl set image deployment/<name> app=<new-image> -n <namespace>
kubectl rollout status deployment/<name> -n <namespace>
```
**Deployment YAML 骨架:**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
namespace: default
spec:
replicas: 3 # 期望副本数
strategy:
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 1 # 更新时最多比期望多 1 个 Pod
maxUnavailable: 0 # 更新时不允许不可用
selector:
matchLabels:
app: my-service
template: # Pod 模板
metadata:
labels:
app: my-service
spec:
containers:
- name: app
image: registry/my-service:v1.2.0
ports:
- containerPort: 8080
```
### Service 类型详解
Service 是 Pod 的"稳定门面",不同 type 决定了流量如何到达 Pod:
| type | 工作原理 | 适用场景 |
|------|---------|---------|
| **ClusterIP**(默认) | K8s 内部虚拟 IP,仅集群内可访问 | 服务间调用,不对外暴露 |
| **NodePort** | 在每个节点上开放一个端口,外部可通过 `<NodeIP>:<port>` 访问 | 快速测试、开发环境验证 |
| **LoadBalancer** | 对接云厂商 LB 自动创建公网 IP | 云上生产环境 |
| **Ingress** | 七层路由,基于域名/路径分发到不同 Service | 多服务共享入口,HTTPS 终结 |
> [!warning] NodePort vs Ingress
> NodePort 只做到四层 TCP/UDP 负载均衡——无法按域名路由。如果你的应用需要 `api.example.com` → Service-A、`web.example.com` → Service-B 这种能力,必须用 **Ingress**。xinfra 中通常配合 Nginx Ingress Controller 使用。
**Ingress 示例:**
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-service-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true" # 强制 HTTPS
spec:
rules:
- host: api.myapp.company.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service # 对应 ClusterIP Service
port:
number: 80
```
---
### kubectl 速查手册
日常运维最高频的命令组合:
```bash
# ── 查看 ──
kubectl get pods -A # 所有 namespace 的 Pod
kubectl get svc,ingress -n prod # 查看 Service + Ingress
kubectl top pod -n staging --containers # 容器级 CPU/Memory 用量
# ── 调试 ──
kubectl exec -it <pod> -- /bin/sh # 进入容器 Shell
kubectl logs -f <pod> -c <sidecar-name> -n ns # 查看指定 Sidecar 日志
kubectl describe pod <pod> -n ns # 查看详细事件(调度失败原因等)
# ── 资源操作 ──
kubectl rollout undo deployment/<name> -n ns # 回滚到上一版本
kubectl scale deployment/<name> --replicas=5 -n ns # 手动扩缩容
kubectl patch deployment/<name> -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"new:v2"}]}}}}' -n ns # 热更新镜像
```
> [!tip] `-A` 标志 = `--all-namespaces`,当你不确定资源在哪个 namespace 时非常有用。但正式脚本中不建议滥用,容易造成误操作。
---
## 常见陷阱与最佳实践
### 1. Resource Limit 缺失
不设置 `resources.limits` 的 Pod 在节点资源紧张时会抢占其他 Pod 资源,导致雪崩:
```yaml
# 推荐:每个容器都要设请求值和上限
containers:
- name: app
resources:
requests: { cpu: "100m", memory: "128Mi" } # 调度依据
limits: { cpu: "500m", memory: "512Mi" } # 硬上限
```
### 2. Liveness vs Readiness 混淆
- **livenessProbe**:判断容器是否"活着",失败则重启 Pod
- **readinessProbe**:判断容器能否接收流量,失败则从 Service 后端摘除
```yaml
# 正确姿势:两个都设,readiness 更激进(快判快入),liveness 更保守
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 3
periodSeconds: 5
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 10
```
### 3. 频繁查看日志的习惯
```bash
# 实时跟踪单个 Pod 日志
kubectl logs -f <pod-name> -n <namespace>
# 如果是重启过的 Pod,看上一次容器的日志
kubectl logs -f <pod-name> -n <namespace> --previous
```
### 4. CrashLoopBackOff — 最常见的 Pod 异常状态
Pod 反复崩溃重启时,排查顺序如下:
```mermaid
flowchart TD
S[CrashLoopBackOff] --> C1{kubectl describe pod<br/>看 REASON}
C1 -->|ImagePullBackOff| I1[检查镜像仓库可达性 + ImagePullSecrets]
C1 -->|OOMKilled| M1[调大 memory limit / 排查内存泄漏]
C1 -->|CreateContainerConfigError| CM1[检查 ConfigMap/Secret 是否存在]
C1 -->|ErrImagePull| E1[镜像 tag 错误或 registry 认证失败]
C1 -->|Initialized/Ready/-/Running| LOGS[kubectl logs -p]
LOGS --> APP[应用自身启动失败?]
APP -->|Yes| FIX[检查启动参数 + 依赖服务连接性]
APP -->|No| DEEP[深入 containerd/containerd-shim 日志]
```
### 5. Ingress 404 或无法访问
开发环境常见"Service 存在但外部打不通"的问题,排查清单:
1. **Nginx Ingress Controller Pod 是否 Running** → `kubectl get pods -n ingress-nginx`
2. **Ingress 资源是否有 `loadBalancer.ip`** → `kubectl get ingress -n <ns>`(若无 IP,可能云厂商 LB 未就绪)
3. **Endpoint 是否有地址** → `kubectl get endpoints <service-name> -n <ns>`(为空说明没有 Ready 的 Pod 匹配)
4. **DNS 解析是否正确** → 内网环境可能需要手动配置 hosts 或使用 CoreDNS
## 关联笔记
以下文档构成了 xinfra 容器化部署的知识链:
- [[technical/xinfra-preview/wayne-overview]] — Wayne 上层的 YAML 模板就是基于这些 K8s 对象
- [[technical/xinfra-preview/cachecloud-overview]] — CacheCloud 管理 Redis,其宿主机未来可能迁移至 K8s 部署
- [[technical/xinfra-preview/cloud-dm-overview]] — K8s 内的 MySQL 实例由 CloudDM 做 SQL 审核
- [[technical/xinfra-preview/ansible-playbook-basics]] — Ansible Playbook 负责 RKE2 集群的底层裸机/VMAutomation
+152
View File
@@ -0,0 +1,152 @@
---
tags: [wayne, preview, infra]
create time: 2026-07-04 12:00
---
# Wayne 多集群容器管理平台
## 概述
Wayne(由 360 开源)是一个基于 Web 的多集群 Kubernetes 容器管理平台,支持通过可视化的方式创建、管理 Deployment 和 Service。xininfra 平台将其作为核心的**服务发布入口**——开发同学无需直接操作 kubectl,通过 Wayne UI 或 API 即可完成服务部署。
> [!info] 背景
> Wayne 已大规模服务于 360 搜索内部,稳定管理近千个业务、上万个容器,运行两年以上。命名源于 DC 漫画角色「Bruce Wayne」(蝙蝠侠)。
## 核心概念
### 架构概览
```mermaid
graph LR
subgraph 前端
UI["Web UI / Angular + Ace Editor"]
end
subgraph 后端
API["API Server / Beego (Go)"]
Worker["Worker / 异步任务"]
DB[("MySQL - 持久化存储")]
end
subgraph 底层
K8s1[K8s Cluster A]
K8s2[K8s Cluster B]
K8N[...]
end
UI --> API
API <--> DB
API -->|"Client-Go"| K8s1
API -->|"Client-Go"| K8s2
API -->|"消息队列"| Worker
Worker -->|"Audit / Webhook"| K8s1
```
核心交互:
- **前端**:Angular 框架 + Ace Editor 实现 YAML 模板编辑,提供表单式部署界面
- **后端**:Beego(Go 框架)处理 HTTP API → Client-Go 操作 K8s API,数据持久化到 MySQL
- **Worker**:基于消息队列的异步任务组件,负责审计日志、Webhook 通知等
- **数据库**:存储项目/用户/部门权限关系、YAML 模板版本历史、发布记录与审计数据
### 核心模型
| 模型 | 说明 |
|------|------|
| **Project** | 项目级分组,相当于 Git Repo 的映射 |
| **Cluster** | 注册的 K8s 集群连接信息(kubeconfig) |
| **Namespace** | 集群内的逻辑隔离空间 |
| **Environment** | 环境(dev / staging / prod),用于区分不同阶段的部署 |
| **Deployment** | 应用层面的容器编排配置 |
| **Service** | 网络层面的服务暴露配置 |
### Wayne 与 K8s 对象的对应关系
```
Wayne Deployment YAML ──→ K8s Deployment + ConfigMap + Ingress
Wayne Service YAML ──→ K8s Service
```
Wayne 对这两种资源的 YAML 做了**标准化封装**,内置了一套模板体系:
```yaml
# Wayne 中的 Deployment 模板骨架
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
spec:
replicas: {{ .Env.Replicas | default 2 }}
template:
spec:
containers:
- name: {{ .ContainerName }}
image: {{ .Image.Repository }}:{{ .Image.Tag }}
ports:
- containerPort: {{ .Container.Ports }}
```
### Wayne 核心能力
| 能力 | 说明 | 对你的意义 |
|------|------|-----------|
| **RBAC 权限管理** | 用户通过角色关联部门与项目,部门角色 vs 项目角色分离 | 多租户场景下按团队划分资源边界 |
| **简化 K8s 对象创建** | 基础模式(表单+模板)+ 高级模式(直接编辑 JSON/YAML) | 降低业务接入成本,无需记忆 kubectl 命令 |
| **多集群管理** | 统一入口管理多个 K8s 集群,可针对集群定制配置 | xininfra 跨机房、跨集群部署的基础 |
| **完整审计模块** | 每次操作留痕,支持自定义 Webhook 回调 | 故障追溯、合规检查的关键支撑 |
| **发布历史与回滚** | 保存所有版本发布记录,一键回滚或基于旧版更新 | 生产安全的底线保障 |
| **Web Shell** | 基于严格权限校验的 Pod 远程终端 | 排查线上问题的快捷通道 |
| **资源报表** | 各项目资源使用占比、上线频次等图表 | 容量规划与成本分摊依据 |
| **站内通知系统** | 管理员推送集群公告、故障报告等 | 信息同步通道 |
| **APIKey 开放接口** | 用户可自助申请 APIKey 管理自己的部门/项目,运维可获取全局 APIKey 进行批量操作 | CI/CD 自动化的基础凭证 |
### 认证登录模式
Wayne 支持三种认证方式,适应不同企业的集成需求:
| 模式 | 适用场景 |
|------|---------|
| **DB 内置** | 独立运行,不依赖外部认证服务 |
| **LDAP** | 与企业组织架构打通,同步部门用户 |
| **OAuth 2.0** | 对接已有 SSO / Identity Provider |
> [!warning] 实习环境
> xininfra 目前使用 DB 模式 + LDAP 混合架构。开发同学通过 LDAP 账号登录 Wayne,但权限分配由 xininfra 侧统一管控。
### API 对接思路
Wayne 提供 RESTful API,适合 CI/CD 流水线自动化调用:
```bash
# 1. 登录获取 token
curl -X POST http://wayne-host/api/user/token/ \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "wayne"}'
# 2. 基于模板创建 Deployment(GitLab CI 场景)
curl -X POST http://wayne-host/api/project/{project-id}/environment/{env-id}/deployment/template/ \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"templateName": "my-service", ...}'
```
> [!tip] GitLab CI 对接 Wayne 的设计方向
> 实习目标是开发 GitLab CI 自动触发 Wayne API 创建 Deployment 和 Service,流程大致为:
> `CI 编译 → 镜像推送到 Registry → CI 调 Wayne API 传入镜像 tag → Wayne 执行部署`
## 常见陷阱与最佳实践
### 1. 权限模型要提前规划
Wayne 的 Project → Environment → Namespace 三级结构容易与实际的 K8s 权限边界不对齐。**建议**:每个业务团队一个 Project,按环境划分 Environment,避免多人混用同一命名空间。
### 2. YAML 模板版本管理
Wayne 允许保存多个 Deployment Template 版本。生产环境的模板应该锁定版本并纳入代码仓库,避免「今天能跑明天不行」的问题。
### 3. APIKey 权限边界要明确
运维人员的全局 APIKey 拥有所有集群和资源的操作权限。**原则**:给 CI/CD 流水线分配最小必要范围的 APIKey,限制其可操作的 Project 和环境范围,避免凭证泄漏导致大面积影响。
## 延伸阅读
- [[technical/xinfra-preview/k8s-rke2-fundamentals]] — Wayne 上层的 K8s 对象知识基础
- [[technical/xinfra-preview/cloud-dm-overview]] — Wayne 部署的服务通常依赖 CloudDM 管理的 MySQL
- [[technical/xinfra-preview/cachecloud-overview]] — Wayne 部署的服务可能使用 CacheCloud 管理的 Redis
- https://github.com/Qihoo360/wayne — Wayne GitHub 仓库
+48
View File
@@ -59,5 +59,53 @@ create time: 2026-07-01 00:00
- [ ] Go(GOPROXY)配置完成并验证
- [ ] 跑通至少一个 Coding Agent,配置好 GitHub CLI
## 五、项目8 · xinfra 预备知识
### 项目背景
xininfra 是公司的统一基础设施平台(Project 8),旨在解决因多条业务线长期独立建设带来的技术债:网络平面混杂(BGP/NAT/macvlan 并存)、K8s 方案多套(KubeSphere/KubeVirt/裸集群/EKS)、存储异构(Ceph/OpenEBS)、发布体系手工操作多、多云资源分散管理等。平台以七机房容器资源池化为核心,配套 CI/CD 双引擎、数据库平台、WAF 安全态势、大内网互联和多云成本看板,系统性重建公司基础设施底座。
### 技术栈与学习资源
以下工具和平台需在开营前初步了解其概念和基本用法,详细知识点可参考下方对应预习笔记:
| # | 工具 | 用途 | 预习笔记 |
|---|------|------|----------|
| 1 | Wayne | 多集群容器管理和发布平台 | [[technical/xinfra-preview/wayne-overview]] |
| 2 | Rancher RKE2 / K8s | 容器编排底层 | [[technical/xinfra-preview/k8s-rke2-fundamentals]] |
| 3 | CloudDM (open-cdm) | 数据库管理及 SQL 审核平台 | [[technical/xinfra-preview/cloud-dm-overview]] |
| 4 | CacheCloud | Redis 管理平台 | [[technical/xinfra-preview/cachecloud-overview]] |
| 5 | Ansible & Playbook | 自动化部署工具 | [[technical/xinfra-preview/ansible-playbook-basics]] |
入口导航:[[xinfra-preview]] — xinfra 预习知识地图
> [!tip] 预习建议
> 不必深入源码,重点理解:**每个工具的定位**(解决什么问题)、**基本架构**(有哪些组件)、**典型使用流程**(如部署一个服务的完整链路)。
### 学习目标
#### 基础目标
1. 熟练搭建 **RKE2 K8s 集群**,能基于 YAML 和 `kubectl` 部署服务
2. 熟悉 **Wayne 多集群容器管理平台**的安装部署,能基于 Wayne 进行服务发布
3. 熟悉 Wayne 的 API 及库表,基于 Deployment 和 Service 部署 YAML 模板,开发 GitLab CI 对接 Wayne 自动创建部署和服务
4. 熟悉 **CloudDM 审核平台**及 **CacheCloud Redis 管理台**部署,能通过这两个平台进行 SQL 上线和 Redis 实例开启
5. 基于 Wayne + CloudDM + CacheCloud 的 API 来实现 XINFRA 统一服务管理平面
#### 进阶目标
1. 熟练使用 **Ansible Playbook** 来部署 MySQL Server、PostgreSQL、Redis Cluster
2. 创建 xininfra 统一服务管理页面,可通过页面直接创建 MySQL 主从、PostgreSQL 主从、Redis Cluster
### 打卡清单
- [ ] 了解 Wayne 项目,阅读 README 并理解其架构
- [ ] 浏览 RKE2 / K8s 文档,熟悉基本概念(Cluster、Deployment、Service、Pod)
- [ ] 了解 CloudDM 和 CacheCloud 的基本功能和定位
- [ ] 学习 Ansible Playbook 基础语法(hosts、tasks、roles)
- [ ] 确认 Go 或 Python + Flask 至少其一的开发环境可用
- [ ] 通读基础目标和进阶目标,明确后续实习期间的能力要求
## 关联笔记
- [[technical/mac-dev-env-setup]] — macOS 开发环境详细配置指南
- [[xinfra-preview]] — xininfra 预习知识地图(5 篇预习笔记入口)