使用Kubernetes+Azure AAD搭建多租户版OpenHands系统

概述

本教程将指导您如何使用 OpenHands、Kubernetes、Ingress 和账户管理系统(接入微软 Azure Active Directory)搭建一个多租户版的 OpenHands 系统。通过本教程,您将实现:

  1. 多用户支持:每个用户登录后访问自己的 OpenHands 实例。
  2. 身份验证:通过 Azure AD 实现用户身份验证。
  3. 动态实例分配:根据用户动态分配 OpenHands 实例。
  4. 数据隔离:确保每个用户的实例和数据互不干扰。

系统架构

系统架构如下:

  1. 用户通过浏览器访问系统。
  2. 系统引导用户通过 Azure AD 登录。
  3. 登录成功后,系统根据用户 ID 查询或分配 OpenHands 实例。
  4. 用户被路由到对应的 OpenHands 实例。
[用户] --> [账户管理系统] --> [Azure AD 登录]
       --> [用户实例映射表] --> [Ingress/反向代理] --> [OpenHands 实例]

准备工作

  1. Docker 镜像:确保 OpenHands 的 Docker 镜像已构建并可用(例如 docker.all-hands.dev/all-hands-ai/openhands:0.27)。
  2. Kubernetes 集群:确保 Kubernetes 集群已配置好(可以使用 Minikube、Kind 或云服务如 GKE、EKS 等)。
  3. kubectl 工具:确保本地安装了 kubectl 并已连接到 Kubernetes 集群。
  4. Azure AD 应用:在 Azure AD 中注册一个应用,用于用户身份验证。

步骤 1: 部署 OpenHands 到 Kubernetes

创建 Kubernetes 配置文件

以下是一个示例的 Kubernetes 配置文件,包含 Deployment 和 Service:

openhands-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: openhands-deployment
  labels:
    app: openhands
spec:
  replicas: 3  # 启动 3 个实例,可以根据需要调整
  selector:
    matchLabels:
      app: openhands
  template:
    metadata:
      labels:
        app: openhands
    spec:
      containers:
      - name: openhands
        image: docker.all-hands.dev/all-hands-ai/openhands:0.27
        ports:
        - containerPort: 3000
        env:
        - name: SANDBOX_RUNTIME_CONTAINER_IMAGE
          value: "docker.all-hands.dev/all-hands-ai/runtime:0.27-nikolaik"
        - name: LOG_ALL_EVENTS
          value: "true"
        volumeMounts:
        - name: openhands-state
          mountPath: /.openhands-state
      volumes:
      - name: openhands-state
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: openhands-service
spec:
  selector:
    app: openhands
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: LoadBalancer

部署到 Kubernetes

将上述配置文件保存为 openhands-deployment.yaml,然后运行以下命令:

kubectl apply -f openhands-deployment.yaml

验证部署

  • 检查 Pod 是否运行正常:
    kubectl get pods
    
  • 检查 Service 是否分配了外部 IP(如果使用 LoadBalancer 类型):
    kubectl get service openhands-service
    
    您可以通过分配的外部 IP 访问 OpenHands 实例。

步骤 2: 配置 Ingress 实现路由

使用 Kubernetes 的 Ingress 控制器,通过不同的子路径或子域名分配实例。例如:

  • 用户 A 访问 http://example.com/user-a
  • 用户 B 访问 http://example.com/user-b

示例 Ingress 配置

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: openhands-ingress
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /user-a
        pathType: Prefix
        backend:
          service:
            name: openhands-user-a
            port:
              number: 80
      - path: /user-b
        pathType: Prefix
        backend:
          service:
            name: openhands-user-b
            port:
              number: 80

将上述配置保存为 openhands-ingress.yaml,然后运行:

kubectl apply -f openhands-ingress.yaml

步骤 3: 接入 Azure AD 实现身份验证

注册 Azure AD 应用

  1. 登录 Azure Portal
  2. 在 Azure AD 中注册一个新的应用:
    • 设置重定向 URI,例如 https://example.com/auth/callback
    • 获取应用的 Client IDClient Secret
  3. 配置 API 权限,确保应用有 openidprofile 权限。

配置身份验证中间件

在账户管理系统中,使用支持 OAuth 2.0 或 OIDC 的库(如 Python 的 authlib)来处理用户登录。

示例代码(Python Flask 应用)

from flask import Flask, redirect, url_for, session
from authlib.integrations.flask_client import OAuth

app = Flask(__name__)
app.secret_key = 'random_secret_key'
oauth = OAuth(app)

azure = oauth.register(
    name='azure',
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    server_metadata_url='https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0/.well-known/openid-configuration',
    client_kwargs={
        'scope': 'openid profile email',
    }
)

@app.route('/')
def home():
    return 'Welcome to OpenHands! <a href="/login">Login with Azure AD</a>'

@app.route('/login')
def login():
    redirect_uri = url_for('auth_callback', _external=True)
    return azure.authorize_redirect(redirect_uri)

@app.route('/auth/callback')
def auth_callback():
    token = azure.authorize_access_token()
    user_info = token.get('userinfo')
    session['user'] = user_info
    return f'Hello, {user_info["name"]}!'

if __name__ == '__main__':
    app.run()

步骤 4: 用户与 OpenHands 实例的映射

数据库存储映射关系

使用数据库(如 PostgreSQL 或 MongoDB)存储用户与实例的映射。例如:

用户 ID (Azure AD)OpenHands 实例 URL
user1@domain.comhttp://openhands-1.com
user2@domain.comhttp://openhands-2.com
user3@domain.comhttp://openhands-3.com

动态路由

使用 Ingress 或反向代理(如 Nginx)根据用户的身份动态路由到对应的实例。

示例 Nginx 配置

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://openhands-instance-$user_id;
    }
}

注意事项

  1. 单用户限制:OpenHands 默认是单用户模式。如果需要支持多用户共享一个实例,可能需要对 OpenHands 的代码进行修改。
  2. 安全性
    • 确保所有通信使用 HTTPS。
    • 配置 Azure AD 的权限范围,避免过多权限暴露。
  3. 扩展性
    • 使用 Kubernetes 的自动扩展功能,根据用户数量动态扩展实例。
    • 定期清理未使用的实例,优化资源利用。

通过本教程,您可以成功搭建一个多租户版的 OpenHands 系统。如果您有任何问题或需要进一步的帮助,请随时联系!

留言与讨论