在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.service
和 webui.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)
共同学习,写下你的评论
评论加载中...
作者其他优质文章