在云计算领域,多租户架构已经成为 SaaS 应用的主流设计模式。它允许不同客户共享同一套基础设施,同时确保数据和资源的隔离性。
多租户是一种软件架构模式,允许多个客户(租户)共享同一软件实例和硬件资源,同时确保数据和配置的隔离性。其优势包括降低成本、提高资源利用率和增强系统的可扩展性。
资源隔离确保每个租户的计算、存储和网络资源互不干扰。实现技术包括虚拟化技术、数据库隔离和内存/CPU 隔离。
配额管理是对每个租户可用资源的定量控制,防止资源过度消耗。它涉及资源分配策略、监控与调整机制。
MCP 架构由租户管理、资源隔离层、配额管理系统、API 网关和监控与日志模块组成。各模块功能及关键技术如下表:
模块名称 | 功能描述 | 关键技术 |
---|---|---|
租户管理 | 负责租户的注册、认证和上下文信息管理。 | JWT 用于租户身份验证,上下文存储在 Redis 中。 |
资源隔离层 | 实现计算、存储和网络资源的隔离。 | Docker 容器、MySQL 多模式策略、Linux Cgroups。 |
配额管理系统 | 定义和执行资源配额策略,监控资源使用情况。 | 基于 Redis 的计数器、Kubernetes 资源 quota 机制。 |
API 网关 | 提供统一入口,处理请求路由和负载均衡。 | Spring Cloud Gateway、Zuul 动态路由配置。 |
监控与日志 | 监控系统性能和资源使用,记录操作日志。 | Prometheus 和 Grafana、ELK Stack。 |
采用共享数据库、分隔模式的策略,每个租户拥有独立的数据库模式。敏感数据通过应用层加密确保安全。
API 设计遵循 RESTful 原则,通过 API 网关统一管理。每个请求需携带租户标识,通常放在 HTTP 请求头中。
使用 Docker 容器技术实现计算资源隔离。每个租户的应用实例运行在独立的 Docker 容器中,通过 Linux 内核的命名空间和控制组进行隔离。
Docker 配置示例(docker-compose.yml):
version: '3.8'
services:
app-tenant1:
image: ourapp:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.2'
memory: 256M
environment:
- TENANT_ID=TNT12345
- DB_SCHEMA=tenant1
networks:
- app-network
restart: unless-stopped
app-tenant2:
image: ourapp:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.3'
memory: 512M
environment:
- TENANT_ID=TNT67890
- DB_SCHEMA=tenant2
networks:
- app-network
restart: unless-stopped
networks:
app-network:
driver: bridge
采用数据库模式隔离策略。每个租户在共享数据库中拥有独立的模式,模式内包含该租户的所有表结构。
存储过程示例(MySQL):
DELIMITER //
CREATE PROCEDURE CreateTenantSchema(IN p_tenant_id VARCHAR(255))
BEGIN
SET @schema_name = CONCAT('tenant_', p_tenant_id);
SET @stmt = CONCAT('CREATE SCHEMA IF NOT EXISTS ', @schema_name);
PREPARE dynamic_statement FROM @stmt;
EXECUTE dynamic_statement;
DEALLOCATE PREPARE dynamic_statement;
-- 创建用户表
SET @stmt = CONCAT(
'CREATE TABLE IF NOT EXISTS ', @schema_name, '.user (',
'id INT PRIMARY KEY AUTO_INCREMENT,',
'username VARCHAR(255) NOT NULL,',
'password_hash VARCHAR(255) NOT NULL,',
'email VARCHAR(255),',
'created_at DATETIME DEFAULT CURRENT_TIMESTAMP'
')'
);
PREPARE dynamic_statement FROM @stmt;
EXECUTE dynamic_statement;
DEALLOCATE PREPARE dynamic_statement;
END //
DELIMITER ;
通过 Kubernetes 的命名空间和网络策略实现网络资源隔离。每个租户的应用部署在独立的命名空间中,并通过网络策略控制不同命名空间之间的通信。
Kubernetes 网络策略示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: tenant-isolation
namespace: tenant1-namespace
spec:
podSelector:
matchLabels:
role: app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: "TNT12345"
egress:
- to:
- namespaceSelector:
matchLabels:
tenant: "TNT12345"
定义每个租户的资源配额,包括 CPU、内存、存储容量等。配额分配基于租户级别或支付计划。
配额定义示例(YAML 格式):
tenants:
- id: TNT12345
name: Acme Corp
plan: Premium
quotas:
cpu: 2000m # 2 CPUs
memory: 4Gi
storage: 100Gi
api_rate_limit: 1000/minute # 每分钟 1000 次 API 调用
concurrent_users: 50
- id: TNT67890
name: Small Business Inc
plan: Basic
quotas:
cpu: 500m # 0.5 CPU
memory: 512Mi
storage: 10Gi
api_rate_limit: 100/minute
concurrent_users: 5
使用 Prometheus 和 Grafana 实现实时监控。定义监控指标并设置告警规则。
Prometheus 配置示例:
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: ourapp
- source_labels: [__meta_kubernetes_namespace]
target_label: tenant_namespace
- source_labels: [__meta_kubernetes_pod_label_tenant_id]
target_label: tenant_id
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
rules:
- alert: TenantCPUUsageHigh
expr: |
sum by (tenant_id) (rate(container_cpu_usage_seconds_total{container="app", tenant_id!=""}[5m]))
/
sum by (tenant_id) (container_spec_cpu_quota{container="app", tenant_id!=""} / 100000)
> 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage for tenant {{ $labels.tenant_id }}"
description: "CPU usage is above 80% for more than 5 minutes."
提供 API 允许管理员动态调整租户配额。调整过程涉及更新配额定义、重新配置 Kubernetes 资源限制以及更新数据库记录。
配额调整 API 示例(Python Flask):
from flask import Flask, request, jsonify
import kubernetes.client
from kubernetes.client.rest import ApiException
app = Flask(__name__)
@app.route('/api/quotas/<tenant_id>', methods=['PUT'])
def update_quota(tenant_id):
data = request.json
cpu_quota = data.get('cpu')
memory_quota = data.get('memory')
# 更新 Kubernetes 资源配额
try:
api_instance = kubernetes.client.AppsV1Api()
deployment = api_instance.read_namespaced_deployment(
name=f"app-{tenant_id}",
namespace=f"tenant-{tenant_id}"
)
deployment.spec.template.spec.containers[0].resources.limits['cpu'] = cpu_quota
deployment.spec.template.spec.containers[0].resources.limits['memory'] = memory_quota
api_instance.replace_namespaced_deployment(
name=f"app-{tenant_id}",
namespace=f"tenant-{tenant_id}",
body=deployment
)
except ApiException as e:
return jsonify({"error": f"Kubernetes API error: {e}"}), 500
# 更新数据库中的配额记录
# (此处省略数据库更新代码)
return jsonify({"status": "success", "message": "Quota updated successfully"}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
准备以下环境和工具:
使用 kubeadm 初始化主节点,并加入工作节点。
# 在主节点上
sudo swapoff -a # 禁用交换分区
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
# 配置 kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 安装网络插件(例如 Flannel)
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# 在工作节点上
sudo kubeadm join <主节点IP>:<端口> --token <token> --discovery-token-ca-cert-hash <hash>
部署 MySQL 数据库,并创建初始的租户模式。
# 使用 Helm 部署 MySQL
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install mysql bitnami/mysql \
--set auth.rootPassword=secret \
--set auth.password=secret \
--set auth.username=user \
--set auth.database=multitenant
# 连接到数据库并创建初始租户模式
kubectl exec -it <mysql-pod-name> -- bash
mysql -u user -p
# 在 MySQL 中
CREATE SCHEMA tenant_TNT12345;
CREATE SCHEMA tenant_TNT67890;
-- 创建用户表(示例)
USE tenant_TNT12345;
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
USE tenant_TNT67890;
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
exit
exit
使用 Kubernetes Deployment 和 Service 部署 MCP 应用。
# 创建命名空间
kubectl create namespace mcp-system
# 部署 API 网关
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
namespace: mcp-system
spec:
replicas: 2
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
spec:
containers:
- name: gateway
image: our-registry/api-gateway:latest
ports:
- containerPort: 8080
env:
- name: CONFIG_SERVICE_URL
value: http://config-service:8888
- name: AUTH_SERVICE_URL
value: http://auth-service:9999
---
apiVersion: v1
kind: Service
metadata:
name: api-gateway
namespace: mcp-system
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: api-gateway
EOF
# 部署其他微服务(示例)
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
namespace: mcp-system
spec:
replicas: 1
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
spec:
containers:
- name: auth
image: our-registry/auth-service:latest
ports:
- containerPort: 9999
env:
- name: DB_URL
value: jdbc:mysql://mysql:3306/auth?user=user&password=secret
---
apiVersion: v1
kind: Service
metadata:
name: auth-service
namespace: mcp-system
spec:
ports:
- port: 9999
targetPort: 9999
selector:
app: auth-service
EOF
为每个租户创建资源配额和网络策略。
# 创建租户命名空间
kubectl create namespace tenant-TNT12345
kubectl create namespace tenant-TNT67890
# 为每个命名空间打上租户标签
kubectl label namespace tenant-TNT12345 tenant=TNT12345
kubectl label namespace tenant-TNT67890 tenant=TNT67890
# 配置资源配额
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
namespace: tenant-TNT12345
spec:
hard:
cpu: "2"
memory: 4Gi
requests.cpu: "1"
requests.memory: 2Gi
pods: "10"
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
namespace: tenant-TNT67890
spec:
hard:
cpu: "0.5"
memory: 512Mi
requests.cpu: "0.2"
requests.memory: 256Mi
pods: "3"
EOF
# 配置网络策略
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: tenant-isolation
namespace: tenant-TNT12345
spec:
podSelector:
matchLabels:
role: app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: "TNT12345"
egress:
- to:
- namespaceSelector:
matchLabels:
tenant: "TNT12345"
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: tenant-isolation
namespace: tenant-TNT67890
spec:
podSelector:
matchLabels:
role: app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: "TNT67890"
egress:
- to:
- namespaceSelector:
matchLabels:
tenant: "TNT67890"
EOF
为每个租户部署应用实例,并配置资源限制。
# 部署第一个租户的应用
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-tenant1
namespace: tenant-TNT12345
spec:
replicas: 3
selector:
matchLabels:
app: tenant-app
tenant: TNT12345
template:
metadata:
labels:
app: tenant-app
tenant: TNT12345
role: app
spec:
containers:
- name: app
image: our-registry/tenant-app:latest
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 700m
memory: 1.5Gi
env:
- name: TENANT_ID
value: TNT12345
- name: DB_SCHEMA
value: tenant_TNT12345
---
apiVersion: v1
kind: Service
metadata:
name: app-tenant1
namespace: tenant-TNT12345
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: tenant-app
tenant: TNT12345
EOF
# 部署第二个租户的应用
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-tenant2
namespace: tenant-TNT67890
spec:
replicas: 1
selector:
matchLabels:
app: tenant-app
tenant: TNT67890
template:
metadata:
labels:
app: tenant-app
tenant: TNT67890
role: app
spec:
containers:
- name: app
image: our-registry/tenant-app:latest
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 300m
memory: 512Mi
env:
- name: TENANT_ID
value: TNT67890
- name: DB_SCHEMA
value: tenant_TNT67890
---
apiVersion: v1
kind: Service
metadata:
name: app-tenant2
namespace: tenant-TNT67890
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: tenant-app
tenant: TNT67890
EOF
测试环境包括:
设计以下测试场景:
使用 JMeter、Locust 和 Chaos Monkey 进行性能测试和混沌测试。
指标 | 测试值 | 结果分析 |
---|---|---|
平均响应时间 (ms) | 120 ± 15 | 在高并发情况下,响应时间略有增加,但仍在可接受范围内。 |
最大 CPU 使用率 (%) | 78 | CPU 使用率接近配额上限(80%),但未超出,说明资源隔离有效。 |
内存使用量 (MB) | 3500 / 4000 | 内存使用接近配额限制,触发了垃圾回收机制,未出现内存溢出。 |
API 错误率 (%) | 0.3 | 少量错误主要由于网络波动引起,系统整体稳定。 |
指标 | 测试值 | 结果分析 |
---|---|---|
平均响应时间 (ms) | 150 ± 20 | 多租户环境下响应时间有所增加,但仍满足 SLA 要求。 |
资源争用事件次数 | 12 事件 / 小时 | 发生了少量资源争用,但通过调度器调整后迅速恢复,未影响服务质量。 |
配额违规次数 | 0 | 所有租户均未超出其配额限制,配额管理系统工作正常。 |
系统吞吐量 (TPS) | 1200 | 系统在多租户负载下保持了较高的吞吐量,弹性伸缩功能有效。 |
实施多层次数据加密策略:
实现基于角色的访问控制(RBAC)模型,定义不同角色的权限范围。
RBAC 配置示例(Kubernetes):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: tenant-TNT12345
name: tenant-admin
rules:
- apiGroups: ["*"]
resources: ["pods", "services", "deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["*"]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: tenant-TNT12345
subjects:
- kind: User
name: john.doe
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: tenant-admin
apiGroup: rbac.authorization.k8s.io
满足不同行业合规性要求:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。