为了账号安全,请及时绑定邮箱和手机立即绑定

在K8s云集群中搭建自己的Ollama服务

Ollama项目让我们可以在本地运行LLM,无论是无需GPU支持还是带有GPU支持,都能实现高性能。我可以在我的笔记本电脑上运行Llama3.1模型并与之对话(参见《使用Llama构建语音及聊天机器人》),尽管存在一些卡顿。在云端托管Ollama服务可以利用更强的计算能力,从而提高性能。接下来,我将教您如何在Kubernetes(K8s)集群上设置自己的Ollama服务。

此外,我还将 Open WebUI 集成到了集群中。WebUI 是一个优秀的工具,可以用来和您的 Ollama 服务进行交互,并在集群内进行直接通信。通过使用 Nginx ingress,集群可以通过 HTTPS 来暴露 WebUI 控制台和 Ollama API,以提高安全性。Ollama API 也通过 HTTP 基本认证进行了保护。

在开始之前,请确保你有一个K8s集群。任何类型的K8s集群都可以。我根据cks-course-environment教程在Google Cloud上搭建了一个K8s集群,包括3个VM(1个主节点和2个工作节点)。

开始:服务准备项目
创建 Ollama 命名空间(Namespace)

首先,创建一个新命名空间 ollama。所有的资源都会在这个命名空间里创建。

使用以下kubectl命令来创建名为ollama的命名空间:

    kubectl create ns ollama
搞定 Ollama

以下 YAML 文件定义了 Ollama 的部署和提供的服务。Ollama 会将模型拉取到 ollama-storage 卷中,并在每个工作节点上使用固定的文件夹来存储模型文件。为了加快模型的加载速度,您可以在环境变量 PRELOAD_MODELS 中添加多个模型名称,用空格分隔(例如,“mistral llama3.1”)。postStart 命令会在容器创建后立即拉取 PRELOAD_MODELS 中指定的模型。环境变量 OLLAMA_KEEP_ALIVE 定义了 Ollama 在内存中保持模型的时间长度,以避免频繁从磁盘加载。

保存这个 YAML 文件并使用 kubectl 创建它。Ollama pod 将会预加载 llama3.1 模型并在你的集群中运行该模型,通过服务端点的 80 端口暴露。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama
  namespace: ollama
spec:
  replicas: 2
  selector:
    matchLabels:
      name: ollama
  template:
    metadata:
      labels:
        name: ollama
    spec:
      containers:
      - name: ollama
        image: ollama/ollama:0.3.5
        volumeMounts:
          - mountPath: /root/.ollama
            name: ollama-storage
        ports:
        - name: http
          containerPort: 11434
          protocol: TCP
        env:
        - name: PRELOAD_MODELS
          value: "llama3.1"
        - name: OLLAMA_KEEP_ALIVE
          value: "12h"
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh", "-c", "for model in $PRELOAD_MODELS; do ollama run $model \"\"; done"]
      volumes:
      - hostPath:
          path: /opt/ollama
          type: DirectoryOrCreate
        name: ollama-storage
---
apiVersion: v1
kind: Service
metadata:
  name: ollama
  namespace: ollama
spec:
  type: ClusterIP
  selector:
    name: ollama
  ports:
  - port: 80
    name: http
    targetPort: http
    protocol: TCP
设置WebUI界面

接下来,使用下面的 YAML 文件来创建 WebUI 的部署和端点。将数据存储 /app/backend/data 挂载到主机文件夹上。通过将 nodeName 设置为 ollama-worker-1,WebUI 的 pod 将被固定到特定节点上,以确保数据(包括登录信息)得到保存。它通过集群内的 [http://ollama](http://ollama) 端点连接到 Ollama。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webui
  namespace: ollama
spec:
  副本数: 1
  选择器:
    matchLabels:
      name: webui
  模板:
    metadata:
      labels:
        name: webui
    spec:
      卷:
      - 主机路径:
          path: /opt/webui
          type: DirectoryOrCreate
        name: webui-storage
      节点名称: ollama-worker-1
      容器:
      - name: webui
        image: ghcr.io/open-webui/open-webui:main
        卷挂载:
        - mountPath: /app/backend/data
          name: webui-storage
        环境变量:
        - name: OLLAMA_BASE_URLS
          value: "http://ollama"
        端口:
        - name: http
          containerPort: 8080
          protocol: TCP
---
apiVersion: v1
kind: 服务
metadata:
  name: webui
  namespace: ollama
spec:
  类型: ClusterIP
  选择器:
    name: webui
  端口:
  - port: 80
    name: http
    目标端口: http
    protocol: TCP
第2部分:配置访问入口

注:在本上下文中,“访问入口”指的是“ingress”,这是网络或Web服务中的一个技术术语。

我们将通过Nginx ingress使用HTTPS来公开Ollama API和WebUI。WebUI有自己的登录,因此我们还将为Ollama API启用HTTP基本身份验证。

Nginx Ingress控制器

在您的集群中安装Nginx Controller。根据您的集群类型,按照说明进行操作。

创建证书和密钥(或秘密)

注:此处的“密钥”指的是“秘密(如:访问密钥)”。

如果你有知名 CA 颁发的证书,就可以跳过这一步。否则,为了测试目的,你可以创建一个自签名证书即可。以下命令将生成一个包含两个主题备用名(SAN)的证书:ollama.servicewebui.service

openssl 生成一个新的 RSA 密钥对并创建一个自签名证书。

openssl req -newkey rsa:2048 -new -nodes -x509 -days 365 -subj "/CN=example.com" -addext "subjectAltName = DNS:ollama.service,DNS:webui.service" -keyout ollama.key -out ollama.crt

创造秘密

kubectl create secret tls ollama -n ollama --cert ollama.crt --key ollama.key
奥拉玛入口插件

为了启用HTTP基本身份验证,请按照基本认证创建名为basic-auth的秘密,包含用户名和密码。

    # 如果没有安装 htpasswd,请先安装一下  
    # apt install -y apache2-utils  

    $ htpasswd -c auth ollama (运行命令)  
    新密码: (请输入密码)  
    重新输入新密码: (请输入密码)  
    为用户 ollama 设置密码 (运行命令)  

    $ kubectl create secret generic basic-auth --from-file=auth -n ollama (运行命令)

然后用以下 YAML 文件创建 Ollama ingress

apiVersion: networking.k8s.io/v1  
kind: Ingress  # 入口
metadata:  
  name: ollama-ingress  
  namespace: ollama  # 命名空间
  annotations:  
    nginx.ingress.kubernetes.io/rewrite-target: /$2  # 重写目标路径为 /$2
    nginx.ingress.kubernetes.io/auth-type: basic  # 使用基本身份验证
    nginx.ingress.kubernetes.io/auth-secret: basic-auth  
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"  # 需要的基本身份验证
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "90"  
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"  
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"  
    nginx.ingress.kubernetes.io/upstream-keepalive-timeout: "300"  
spec:  
  ingressClassName: nginx  
  tls:  
  - hosts:  
      - ollama.service  
    secretName: ollama  # 密钥名称
  rules:  
  - host: ollama.service  
    http:  
      paths:  
      - path: /()(.*)  
        pathType: Prefix  # 路径类型
        backend:  
          service:  
            name: ollama  # 服务名称
            port:  
              number: 80  # 端口
WebUI 网关

类似的 WebUI 设置,但不包含基本认证。

apiVersion: networking.k8s.io/v1  
kind: Ingress  # Ingress用于管理进入集群的网络流量
metadata:  
  name: webui-ingress  
  namespace: ollama  
  annotations:  
    nginx.ingress.kubernetes.io/rewrite-target: /$2  # 重写目标路径
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "90"  # 代理连接超时时间
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"  # 代理发送超时时间
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"  # 代理读取超时时间
    nginx.ingress.kubernetes.io/upstream-keepalive-timeout: "300"  # 上游保持连接超时时间
spec:  
  ingressClassName: nginx  
  tls:  
  - hosts:  
      - webui.service  
    secretName: ollama  # secretName用于指定TLS证书的名称
  rules:  
  - host: webui.service  
    http:  
      paths:  
      - path: /()(.*)  # 捕获请求路径信息
        pathType: Prefix  # 路径类型为前缀匹配
        backend:  
          service:  
            name: webui  
            port:  
              number: 80

应用 ingress 规则并通过 Nginx 控制器将其暴露。如果你有一个负载均衡器的话,可以通过 443 端口暴露端点。我的入口控制器通过主节点的 32559 端口暴露。我将在接下来的测试中使用这个端口。

    $ kubectl get svc -n ingress-nginx  
    NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE  
    ingress-nginx-controller             NodePort    10.107.134.98   <none>        80:32659/TCP,443:32559/TCP   23h
第三部分:您的电脑设置

如果你有一个注册域名,并且该域名和证书都是由知名的证书颁发机构(CA)颁发的,你可以直接进入第4部分的内容。对于使用自签名证书的人,你需要更新主机文件,并将证书添加到信任存储中。

更新你的主机文件

在 macOS 笔记本电脑上,你需要向 /etc/hosts 文件添加两个条目。将 <IP of the Master Node> 替换为你的主节点实际 IP 地址或负载均衡 IP。

    sudo vim /etc/hosts  
    <主节点的IP>    ollama.service  
    <主节点的IP>    webui.service
更新您的信任列表

由于我们使用的是自签名证书,证书验证会默认失败,因为使用的是自签名证书。虽然你可以通过选项如 curl -k 跳过验证步骤,但这在某些 SDK 和 API 调用中可能不可用,因此这种方法并不总是可行的。最好将证书添加到你所使用的工具的信任存储中,以避免验证失败。

由于我们使用的是自签名证书,证书验证会失败。我们可以使用一些选项跳过验证步骤,例如 curl -k,但这种方法在SDK和API调用中不一定可行。最好还是将证书添加到您所使用工具的信任存储里。

比如说,如果你在用Python,你可以用certifi库来管理信任存储库。

运行命令 pip install certifi

找到 certifi 证书存储的位置,可以这样做:你可以运行以下 Python 代码:

    import certifi  
    print(certifi.where())

这将显示 cacert.pem 文件的位置,这是 certifi 使用的证书库。用文本编辑器打开 cacert.pem 文件,将自签名证书添加到文件末尾。你的证书需要是 PEM 格式,看起来大概像下面的样子:

-----BEGIN CERTIFICATE-----  
MIIDXTCCAkWgAwIBAgIJALa6F+2a6H5TMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV  
...  
-----END CERTIFICATE-----

注:以上为Base64编码的证书内容,无需翻译。

在 `cacert.pem` 文件中保存更改,并添加你的自签名证书。

# 第4部分:试一下

## WebUI

通过WebUI端口访问该Ollama服务。第一个注册的用户将成为管理员。

![](https://imgapi.imooc.com/677b51b9091012b112390510.jpg)

## [LangChain(一个语言链技术平台)](https://www.langchain.com/)

`langChain`库会与Ollama API进行交互。请使用您当初设置Ollama ingress时创建的用户名和密码。
from langchain_community.llms import Ollama  
from requests.auth import HTTPBasicAuth  

auth = HTTPBasicAuth('myusername', 'mypassword')  

ollama = Ollama(  
    base_url='https://ollama.service:32559',  
    model="llama3.1",  
    auth=auth  
)  
print(ollama.invoke("天空为什么是蓝色的"))

## 奥拉玛 Python 库

[Ollama Python](https://github.com/ollama/ollama-python) 在底层使用了 `httpx` 客户端。您需要通过 `httpx.BasicAuth` 方法来生成认证头。

from ollama import Client
import httpx
httpx_auth = httpx.BasicAuth(username="myusername", password="mypassword")
client = Client(host='https://ollama.service:32559', auth=httpx_auth)
response = client.chat(model='llama3.1', messages=[
{
'role': 'user',
'content': '天空为什么是蓝色的?',
}])
print(response)


建议观看流媒体
stream = client.chat(model='llama3.1', messages=[  
  {  
    'role': 'user',  
    'content': '天空为啥是蓝色的?',  
  }], stream=True)  

for chunk in stream:  
  print(chunk['message']['content'], end='', flush=True)


# 结论:

Ollama 提供了一种运行大型语言模型(LLMs)的绝佳途径。通过利用云计算,你可以自己运行一个 LLM 服务,极大地有助于各种 AI 能力的探索。

接下来,你可以用GPU来运行Ollama,可以参考我的文章[在Kubernetes中用Nvidia GPU运行自己的Ollama](https://medium.com/@yuxiaojian/run-your-own-ollama-in-kubernetes-with-nvidia-gpu-8974d0c1a9df)
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消