K3S+Hami+Higress+Vip(nginx+keepalived)搭建云原生高可用的大模型部署平台

由于之前参与了一些私有化+云原生结合的大模型部署和应用的项目,以及团队内部有想推云原生部署AI模型的想法,就尝试在我们测试环境使用K3S+Hami+Higress+Vip(nginx+keepalived)来搭建一套高可用的集群部署平台, 来验证易用性和便捷性。

K3S 这个是一个轻量化的kubernetes,专门用来做容器化部署的

kuboard:v4 (swr.cn-east-2.myhuaweicloud.com/kuboard/kuboard:v4)这一个K3S的数据面板或者说控制台,用来管理K3S的集群的各种部署配置以及资源监测等。

Hami 这个是一个插件,用来给K3S提供GPU资源信息用来管理和调度作用的插件;可以对GPU资源进行显存切分,有了这个组件,多个pod就可以部署在同一个GPU上,不然一张显卡就只能一个pod独占了。

Higress 阿里开发的一个网关组件,支持快速的集成各类LLM 以及 Agent API,提供域名管理、路由配置、AI网关管理、插件配置(流量、安全、认证、转换、AI(包含 AI模型路由、token统计、MCP服务器、AI缓存))------这个正是我们集成openAI API以及 各种不同模型服务化对外暴露能力的一个易用方便的组件

Vip(nginx+keepalived) 使用nginx反向代理+keepalived监控 nginx来实现 VIP漂移,在服务节点出现故障后,自动快速的切换到容灾或者备节点。

一、安装

上面每个组件安装都比较简单,官网都有一个命令执行就能完成安装。我这边是没有自己搭建过,以及当前服务器上运行的又一些服务不能重启服务器这个约束下,尝试安装的。我们的服务器系统都是ubuntu、helm以及网络都都是拉通了,比较麻烦的就是K3S多控制面板集群在安装过单控制面板K3S的机器上怎么安装成功、nginx源码编译安装、hami组件的配置以及higress路由的插件配置,这些地方折腾了一些时间,最久的还是K3S多控制面板集群在安装过单控制面板K3S的机器上怎么安装成功,这个耗时最久。

1、K3S

多control panel集群的安装就2个命令,第一个

bash 复制代码
    curl -sfL https://get.k3s.io | sh -s - server \
    --cluster-init \
    --tls-san=<FIXED_IP> # Optional, needed if using a fixed registration address

在一个节点上执行上述命令,使用--cluster-init来开启etcd(内置数据库服务) ;国内网络的问题也可以使用加速版本的命令

bash 复制代码
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | sh -s - server \
      --cluster-init

注意安装过后,这个节点的机器上/var/lib/rancher/k3s/server,会有一个token文件,其他的节点加入这个集群就需要这个这个token

其他节点加入集群(可以是server 也可以是 agent),在每个节点上都执行一次

bash 复制代码
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | K3S_TOKEN=SECRET sh -s - server \
    --server https://<ip or hostname of server1>:6443

注意如果是非server节点加入,就可以用下面的命令

bash 复制代码
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | K3S_TOKEN=SECRET sh -s - agent \
    --server https://<ip or hostname of server1>:6443

已经部署过单control panel的机器上,多control panel的 安装的时候稍微复杂一点点,就是要把之前所有node上的k3s 所有的服务下掉,同时可以新修改一个K3S_TOKEN. 注意K3S相关的服务就是 k3s server 和 k3s agent,停止它们后,按照上面的命令进行安装和切换。停止使用如下命令:

bash 复制代码
# agent node 停止
sudo systemctl stop k3s-agent

# server node 停止
sudo systemctl stop k3s

上述安装OK后,就可以查看集群的信息了。一些组件k3s也会自动安装,例如kubectl,执行kubectl get nodes,得到如下结果:

2、kuboard:v4 安装

这个是一个dashboard面板、以web形式可以管理集群、查看集群信息、部署之类的。也有k8S官方版本的,但是已经不维护,之前下载安装也没有搞成功。就用了同事推荐的华为的一个版本, 安装使用docker来进行安装,对应的docker-compose详情如下:

bash 复制代码
configs:
  create_db_sql:
    content: |
      CREATE DATABASE kuboard DEFAULT CHARACTER SET = 'utf8mb4' DEFAULT COLLATE = 'utf8mb4_unicode_ci';
      create user 'kuboard'@'%' identified by 'kuboardpwd';
      grant all privileges on kuboard.* to 'kuboard'@'%';
      FLUSH PRIVILEGES;

services:
  db:
    image: swr.cn-east-2.myhuaweicloud.com/kuboard/mariadb:11.3.2-jammy
    # image: mariadb:11.3.2-jammy
    # swr.cn-east-2.myhuaweicloud.com/kuboard/mariadb:11.3.2-jammy 与 mariadb:11.3.2-jammy 镜像完全一致
    environment:
      MARIADB_ROOT_PASSWORD: kuboardpwd
      MYSQL_ROOT_PASSWORD: kuboardpwd
      TZ: Asia/Shanghai
    volumes:
      - ./kuboard-mariadb-data:/var/lib/mysql:Z
    configs:
      - source: create_db_sql
        target: /docker-entrypoint-initdb.d/create_db.sql
        mode: 0777
    networks:
      kuboard_v4_dev:
        aliases:
          - db
  kuboard:
    image: swr.cn-east-2.myhuaweicloud.com/kuboard/kuboard:v4
    # image: eipwork/kuboard:v4
    environment:
      - DB_DRIVER=org.mariadb.jdbc.Driver
      - DB_URL=jdbc:mariadb://db:3306/kuboard?serverTimezone=Asia/Shanghai
      - DB_USERNAME=kuboard
      - DB_PASSWORD=kuboardpwd
    ports:
      - "8000:80"
    volumes:
      - ./kuboard-log:/app/logs:Z
    depends_on:
      - db
    networks:
      kuboard_v4_dev:
        aliases:
          - kuboard

networks:
  kuboard_v4_dev:
    driver: bridge

安装完成后就可以打开网页进行集群管理和操作了。http//ip:port/8000截图如下:

3、Hami 安装

HAMi 是开源的云原生 GPU 虚拟化中间件,为 AI 工作负载提供异构加速器的共享、隔离与调度能力。至此GPU显存和算力的细粒度的切分,低于整卡的切分。有了它Kubernetes才能正常、高效、稳定的进行gpu服务的管理和运行。从官方文档或者github上可以一键式的安装,在线安装命令如下:

bash 复制代码
helm install hami hami-charts/hami --set scheduler.kubeScheduler.imageTag=v1.16.8 -n kube-system

等待一定的时间,安装成功后,kubectl get pods -A 查看hami-device-plugin 和 hami-scheduler是否安装成功,截图如下,我这边的pod正常运行说明就可以使用它来部署模型推理之类的服务了。

部署的pod的时候还是会有一个坑,之前同事有提醒过,但是过了很久才来操作,忘记这个坑,自己折腾很久了才发现和解决。runtimeClassName: nvidia 这个注解在node containerd 的 config.toml中没有提前全局修改为指定的runtime且deployment也没有显示声明,部署会不成功,直接报错。部署示例如下:

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod-hy-test
  annotations:
    # 注解指定显卡id
    nvidia.com/use-gpuuuid: "GPU-5f3b00da-2b13-d66e-f9a4-ad741e987643"
spec:
  runtimeClassName: nvidia  # 必须指定------集群没有指定容器启动的运行时、有可能是华为的、nvidia的
  containers:
    - name: ubuntu-container
      image: ubuntu:18.04
      command: ["bash", "-c", "sleep 86400"]
      resources:
        requests:
          nvidia.com/gpu: 1                      # ✅ 必须添加 requests
          nvidia.com/gpumem: 300      # 单位是 MiB,不要带 "k" 或 "Ki"
        limits:
          nvidia.com/gpu: 1
          nvidia.com/gpumem: 300 # identifies 3000M GPU memory each physical GPU allocates to the pod (Optional,Integer)
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname  # 节点的hostname标签
            operator: In
            values:
            - kxjl-36 

4、Higress 安装

这个组件是阿里开源的一个AI云原生API网关,将流量网关、微服务网关和安全网关三合一,提供统一的服务暴露、流量管控、API 全生命周期管理能力。在K8S中还能丝滑的替换Ingress这个组件。我们这里使用就是需要用到它的路由以及负载均衡能力(我们自身也是有一个路由功能的自研的组件,相当于是在这个组件上再套一层Higree),未来可以扩展安全、流量控制以及一些请求指标的可视化。它是云原生的,安装起来也是一句命令的事情,命令如下:

bash 复制代码
helm repo add higress.io https://higress.io/helm-charts
helm install higress -n higress-system higress.io/higress --create-namespace --render-subchart-notes --set global.local=true --set global.o11y.enabled=false

安装完成后的截图:

由于没有LoadBalancer负载均衡方案以及我不清楚公司的内网DNS解析服务,所以就有必要对higress-console和higress-gateway开启nodeport后,使用nodeip来访问控制台页面和网关服务。

注意这个开启有多种方式,一种是安装的时候修改安装命令指定,还有一种就是安装后的;我这边就是安装后才发现需要使用nodeport来访问的才采取安装后来修改svc的方式。

形如下面的命令,注意 -p 后面的具体的nodeport内容项

bash 复制代码
kubectl patch svc higress-console -n higress-system -p '{"spec":{"type":"NodePort","ports":[{"port":8080,"nodePort":30080}]}}'
bash 复制代码
kubectl patch svc higress-gateway -n higress-system -p '{"spec":{"type":"NodePort","ports":[{"port":80,"nodePort":30081},{"port":443,"nodePort":30082}]}}'

修改完后就可以打开higress的控制台了,浏览器输入 nodeip:nodeport 就可以打开了

左边就菜单栏就可以进行后端服务的管理了,路由、ai服务路由、流量统计、安全管理等

5、Vip(nginx+keepalived)安装

这里是为了预防访问入口的服务器宕机或者挂掉导致客户访问不到集群上的服务了,所以得用高可用容灾的方案。这里采用简单的免费的以及要求不怎么高的方案,使用keepalived开启虚ip,监控不同机器上的nginx,每台机器上的nginx进行反向代理, 把访问虚ip的请求中转分发到后端服务中。以上就是主要的功能和作用,具体就涉及到keepalived的开启vip、对应的监控任务脚本触发vip漂移、和Nginx的安装以及反向代理的配置。

nginx的安装比较简单直接上网搜索一下,我直接把nginx的源码下载路径提供下,安装是基于nginx的源码编译安装的。nginx下载官网地址

使用上面的稳定版本,内容如下:

编译安装(直接看github或者问大模型)

bash 复制代码
./configure \
  --prefix=/usr/local/nginx \
  --with-http_ssl_module \
  --with-http_v2_module \
  --with-http_realip_module \
  --with-http_stub_status_module \
  --with-stream \
  --with-stream_ssl_module



make


sudo make install

运行后的进程

对于keepalived这个工具直接给下配置,安装使用系统命令就好了。/etc/keepalived路径下的keepalived.conf可以完成vip的一些配置。注意这个vip一定是内网段一个没有使用过的ip,可以借助主机的网卡以及keepalived来实现内网直接的漂移。

master

bash 复制代码
global_defs {
   router_id kxjl-31   # 每台机器不同,建议用 hostname
}

# 定义 Nginx 健康检查脚本
vrrp_script check_hy_nginx {
    script "/data02/yanghuang/check_hy_nginx.sh"
    interval 2              # 每 2 秒检查一次
    timeout 5               # 脚本执行超时 5 秒
    weight -20              # 脚本失败时,本机优先级减 20
    fall 2                  # 连续失败 2 次才判定为失败
    rise 1                  # 成功 1 次即恢复
}

vrrp_instance VI_NGINX {
    state MASTER            # 主服务器: MASTER,备用: BACKUP
    interface ens1f0        # 你的网卡名
    virtual_router_id 51    # 主备必须一致
    priority 100            # 主: 100,备: 90(或更低)
    advert_int 1            # 心跳间隔 1 秒
    
    authentication {
        auth_type PASS
        auth_pass 1111      # 主备必须一致
    }
    
    # 虚拟IP
    virtual_ipaddress {
        x.x.x.188/24    # 你的 VIP
    }
    
    # 关联监控脚本
    track_script {
        check_hy_nginx
    }
    
}

backup

bash 复制代码
! Configuration File for keepalived

global_defs {
   router_id kxjl-32   # 每台机器不同,建议用 hostname
}

# 定义 Nginx 健康检查脚本
vrrp_script check_hy_nginx {
    script "/data02/yanghuang/check_hy_nginx.sh"
    interval 2              # 每 2 秒检查一次
    timeout 5               # 脚本执行超时 5 秒
    weight -20              # 脚本失败时,本机优先级减 20
    fall 2                  # 连续失败 2 次才判定为失败
    rise 1                  # 成功 1 次即恢复
}

vrrp_instance VI_NGINX {
    state BACKUP            # 主服务器: MASTER,备用: BACKUP
    interface ens1f0        # 你的网卡名
    virtual_router_id 51    # 主备必须一致
    priority 90            # 主: 100,备: 90(或更低)
    advert_int 1            # 心跳间隔 1 秒
    
    authentication {
        auth_type PASS
        auth_pass 1111      # 主备必须一致
    }
    
    # 虚拟IP
    virtual_ipaddress {
        y.y.y.188/24    # 你的 VIP
    }
    
    # 关联监控脚本
    track_script {
        check_hy_nginx
    }
    
}

查看vip目前是否在本机上ip add show ens1f0 | grep 188, 31上有结果

bash 复制代码
inet x.x.x.188/24 scope global secondary ens1f0

说明vip目前在31服务器上

二、部署实践

在上述K3S相关的组件都搭建完毕后,结合我司现有的模型部署以及服务架构特点,需要部署一个大模型推理服务进行测试验证。调用链是:

用户请求------>keepalived------>VIP------>Nginx------>K3SHigress-gateway------>Nus3------>model service

外部请求流量通过vip进入nginx,nginx通过反向代理进入K3S的Higress-Gateway服务(通过Nodeport),然后Higress通过配置的路由把,请求转发到Nus3这个组件;Nus3在把请求转发到底层的model service,这个是通过请求中的请求体有关字段来确定转发的目标服务。架构中有一个隐藏的点就是底层模型服务通过service name 向nus3 组件发送心跳(http post 请求,上报pod ip 和port),这里由于是K3S内 ClusterIP DNS服务以及Iptables 做的tcp的负载和转发,上报心跳的服务是长连接就会导致pod粘连的问题------解决的办法就是修改为短连接或者nus3组件的服务采用headless的形式,在其他pod内手动发现nus3的pod ip,请求级别的做负载均衡,就不会导致pod粘连了。

1、模型服务部署

采用vllm框架以及sidecar 容器把模型能力上报到nus中,另外模型权重的挂载并没有采用任何pvc的类型例如local-path和longhorn-path这类单节点和分布式的存储,类似和docker -v 一样的 hostPath方案:

bash 复制代码
      containers:
        - name: dialog-agent-model
          image: 172.19.13.36:18182/ai/vllm-openai:v0.23.0   # 替换为你的实际镜像地址
          volumeMounts:
          - name: kxjl-models-storge
            mountPath: /model

          

      volumes:
        - name: kxjl-models-storge
          hostPath:
            path: /data02/yanghuang/deployment/docker/chat_sglang/merged_models/dialog_agent/merge
            type: Directory

模型权重的挂载就类似上述方案,一般规范性的使用还是要使用PVC,底层使用NFS做到分布式多节点高可用。

当然部署的时候还关注到测试环境显卡可用性,选择固定的显卡以及节点, 所有使用了节点亲和性把服务部署到对应的node,使用注解来实现固定显卡的使用。最终所有的deployment.yml详情如下:configMap、deployment以及service都写在一起了。

bash 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: nus-sidecar-config # ConfigMap 的名称
  namespace: default # 与 Deployment 相同的命名空间
data:
  # 键是文件名,值是文件内容。请将 YOUR_TOML_CONFIG_CONTENT_HERE 替换为 toml.config 文件的实际内容。
  # 注意:如果内容包含特殊字符(如 $, {, }, # 等),可能需要用引号包围或使用 |- 块样式。
  config.yaml: |
    nus: "nus3:8888"
    node_name: "dialogagent.model.20260806"
    openai: 
      api_key: "none"
      base_url: "http://127.0.0.1:8888/v1"

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dialog-agent-model
  namespace: default
  labels:
    app: dialog-agent-model
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dialog-agent-model
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: dialog-agent-model
      annotations:
        # 注解指定显卡id
        nvidia.com/use-gpuuuid: "GPU-a750cfe1-e711-d85f-27f7-aae74bf4c6a3"
    spec:
      runtimeClassName: nvidia  # 必须指定------集群没有指定容器启动的运行时、有可能是华为的、nvidia的
      # 使用私有仓库的 Secret
      imagePullSecrets:
        - name: 36-harbor-secret
      containers:
        - name: dialog-agent-model
          image: ip:port/ai/vllm-openai:v0.23.0   # 替换为你的实际镜像地址
          imagePullPolicy: IfNotPresent
          # command: ["sleep", "infinity"]
          command: ["vllm", "serve"]
          args:
          - "--model"
          - "/model"    # 替换为你的模型路径
          - "--port"
          - "8888"
          - "--host"
          - "0.0.0.0"
          - "--max-num-seqs"
          - "32"
          - "--tensor-parallel-size"
          - "1"
          - "--gpu-memory-utilization"
          - "0.95"
          ports:
          - containerPort: 8888
            name: vllm-http
          volumeMounts:
          - name: kxjl-models-storge
            mountPath: /model
          resources:
            # limits:
            #   nvidia.com/gpu: 1    # 每个 Pod 申请 1 张 GPU 独占的
            # requests:
            #   nvidia.com/gpu: 1    # 每个 Pod 申请 1 张 GPU 独占的
            requests:
              nvidia.com/gpu: 1          # requests 和 limits 必须相同
              nvidia.com/gpumem: 23000   # 单位是 MiB
              cpu: 2
              memory: 10Gi
            limits:
              nvidia.com/gpu: 1         # 申请 1 个 vGPU
              nvidia.com/gpumem: 23000  # 分配 20000 MiB 显存
              cpu: 3
              memory: 12Gi
            # env:
            # - name: VLLM_LOGGING_LEVEL
            #   value: "INFO"
          

        - name: nus-sidecar
          image: ip:port/ai/nus_sidecar:1.0.0   # 替换为你的实际镜像地址
          imagePullPolicy: IfNotPresent
          ports:
          - containerPort: 8887
            name: sidecar-http
          volumeMounts:
            - name: config-volume # 与下面 volumes.name 对应
              #  指定挂载到容器内的路径
              # mountPath: /app/config
              # subPath 允许将 ConfigMap 中的单个文件挂载到已有文件或非空目录下的文件
              
              # mountPath: /app/config/config.json
              # subPath: config.json
              mountPath: /app/config.yaml
              subPath: config.yaml

      volumes:
        - name: kxjl-models-storge
          hostPath:
            path: /data02/yanghuang/deployment/docker/chat_sglang/merged_models/dialog_agent/merge
            type: Directory
        - name: config-volume
          configMap:
            name: nus-sidecar-config # 引用上面创建的
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname  # 节点的hostname标签
                operator: In
                values:
                - kxjl-31 
---
apiVersion: v1
kind: Service
metadata:
  name: dialog-agent-service
  namespace: default
  labels:
    app: dialog-agent-model
spec:
  selector:
    app: dialog-agent-model
  ports:
  - protocol: TCP
    port: 8888          # Service 端口
    targetPort: 8888    # Pod 端口
  type: ClusterIP

kubectl apply -f depolyment.yaml 执行就可以了 也可以在k3s的控制台使用 前端页面来操作:

部署后可以在控制台页面看到很多deployment,我们测试的dialog-agent-model已经部署成功了

2、Higress配置

由于采用higress以及使用了nus和一些自研的组件,如果是想higess自己访问模型服务不走nus也是可以做到的;当然我们可以把nus3以及model service的路由都在Higress中配置好。还有一个点就要住了 model service 配置的时候 使用addProviderHeader: x-higress-llm-provider要特别注意,只有vllm启动的时候,openAI输入model模型参数是带/,模型提供商之类的,它会以/来切割这个参数,把最后的字段作为model参数最后的值输入到模型服务中。

3、调用展示

使用kubectl 来查看 kubectl get pods

-o wide 看详细信息

调用展示

higress------>nus------>model service

中间的nus日志

转发成功并收到响应

也可以直连model服务,使用openAI的调用方案

k3s官网文档

hami官网

相关推荐
团子股股东峥哥1 小时前
day38-RHEL-管理存储堆栈
linux·运维·服务器
工业HMI实战笔记2 小时前
【拯救HMI】:自动化对外输出新范式:从设备出口到 “技术 + 服务” 全链条交付
运维·自动化
Safeploy安策数据2 小时前
密钥管理入门:从泄漏复盘看企业如何守住密钥安全底线
大数据·运维·微服务
闲云野鹤在人间2 小时前
Docker入门|第2章 容器架构详解
linux·运维·docker·容器·架构·云计算
wzq11_6662 小时前
Docker数据卷管理
运维·docker·容器
snow@li2 小时前
服务器运维:Let’s Encrypt 证书 + HTTPS + HTTP/2 全景分析
运维
小牛马爱写博客3 小时前
K8s 中部署WordPress 博客
云原生·容器·kubernetes
snow@li3 小时前
服务器运维:堡垒机与服务器全景关系深度分析(架构、差异、协作、场景)
运维
爱和冰阔落3 小时前
【Linux】手写日志与固定线程池:任务队列、工作线程和安全退出
linux·运维·c++·redis·安卓