Создайте бессерверный экземпляр txtai с помощью Kubernetes и Knative

Разработка бессерверных приложений — это популярный способ для разработчиков избавиться от сложности серверов и сосредоточиться на доставке продуктов своим пользователям. Все популярные облачные провайдеры предлагают форму функция как услуга (FaaS). Примеры включают AWS Lambda, Облачные функции Google и Функции Azure. Цена зависит от времени выполнения и потребляемой памяти. Существует значительная экономия для низкочастотных и среднечастотных функций.

txtai — это платформа с открытым исходным кодом для семантического поиска и рабочих процессов, основанная на языковых моделях. txtai поддерживает создание приложений с конфигурацией YAML с парадигмой сделай один раз, запускай где угодно.

Ряд статей посвящен txtai, ссылки на которые приведены ниже.









В этой статье мы настроим экземпляр векторного поиска txtai (также известный как семантический/похожий/нейронный поиск) в среде на основе Kubernetes с помощью Knative. Этот подход не зависит от облака и позволяет использовать бессерверный подход для любого поставщика облачных услуг. Kubernetes также поддерживает экземпляры графического процессора, необходимые для достижения максимально быстрого времени вывода модели машины.

Примечание. Тем, кто интересуется примером на основе AWS Lambda, ознакомьтесь с этой документацией.

Установите Кубернетес

Первый шаг — установка локального кластера Kubernetes. Доступен ряд вариантов, в том числе вид, миникубе, MicroK8s и K3s. В этой статье используется тип, но можно заменить и другие. Если кластер Kubernetes уже доступен, этот шаг можно пропустить.

Доброжелательная документация великолепна, следуйте инструкциям здесь. Если все сработало, следующее должно вернуть версию, аналогичную приведенной ниже.

$ kind version
kind v0.11.1 go1.16.4 linux/amd64

Установить Кнатив

Далее мы установим Knative. Согласно веб-сайту Knative:

Knative — это решение корпоративного уровня с открытым исходным кодом для создания бессерверных приложений и приложений, управляемых событиями.

По сути, Knative упрощает создание бессерверных приложений в Kubernetes.

Примечание. При установке в существующем кластере Kubernetes пропустите оставшуюся часть этого раздела и вместо этого используйте эти инструкции по установке.

Как и в случае с kind, Knative предоставляет надежную документацию. Следуйте инструкциям здесь, чтобы установить копию Knative. Инструкции здесь устанавливают экземпляр Knative в кластер Kubernetes.

После установки Knative выполните следующие действия.

  1. Установите базовый вид кластера.
$ kind create cluster
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.21.1) 🖼
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

2. Установите Knative в локальный кластер с помощью быстрого запуска.

$ kn quickstart kind
Running Knative Quickstart using Kind
✅ Checking dependencies...
    Kind version is: 0.11.1
☸ Creating Kind cluster...
Creating cluster "knative" ...
 ✓ Ensuring node image (kindest/node:v1.22.4) 🖼
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
 ✓ Waiting ≤ 2m0s for control-plane = Ready ⏳ 
 • Ready after 18s 💚
Set kubectl context to "kind-knative"
You can now use your cluster with:
kubectl cluster-info --context kind-knative
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
🍿 Installing Knative Serving v1.2.0 ...
    CRDs installed...
    Core installed...
    Finished installing Knative Serving
🕸️ Installing Kourier networking layer v1.2.0 ...
    Kourier installed...
    Ingress patched...
    Finished installing Kourier Networking layer
🕸 Configuring Kourier for Kind...
    Kourier service installed...
    Domain DNS set up...
    Finished configuring Kourier
🔥 Installing Knative Eventing v1.2.0 ... 
    CRDs installed...
    Core installed...
    In-memory channel installed...
    Mt-channel broker installed...
    Example broker installed...
    Finished installing Knative Eventing
🚀 Knative install took: 1m38s 
🎉 Now have some fun with Serverless and Event Driven Apps!

3. Если все работает, следующее должно вернуть два кластера, как показано ниже.

$ kind get clusters
kind
knative

Создать txtai образ

Далее мы создадим образ txtai в комплекте с txtai, зависимостями, конфигурацией и кэшированной моделью.

  1. Создайте новый рабочий каталог.
$ mkdir app && cd app

2. Создайте файл config.yml. Это создает индекс в памяти.

# config.yml
writable: true
embeddings:
  path: sentence-transformers/nli-mpnet-base-v2
  content: true

3. Создайте Dockerfile. Этот Dockerfile также можно скачать с GitHub.

# Set base image
ARG BASE_IMAGE=neuml/txtai-cpu
FROM $BASE_IMAGE
# Copy configuration
COPY config.yml .
# Run local API instance to cache models in container
RUN python -c "from txtai.api import API; API('config.yml', False)"
# Start server and listen on all interfaces
ENV CONFIG "config.yml"
ENTRYPOINT ["uvicorn", "--host", "0.0.0.0", "txtai.api:app"]

4. Создайте образ.

Примечание. Префикс тега важен, так как он предотвратит попытки Knative загрузить образ из Docker Hub.

$ docker build -t dev.local/txtai:v1 .
Sending build context to Docker daemon  3.072kB
Step 1/6 : ARG BASE_IMAGE=neuml/txtai-cpu
Step 2/6 : FROM $BASE_IMAGE
 ---> 894803a0dc04
Step 3/6 : COPY config.yml .
 ---> b4b01e846285
Step 4/6 : RUN python -c "from txtai.api import API; API('config.yml', False)"
 ---> Running in 2407b608e382
Downloading: 100%|██████████| 587/587 [00:00<00:00, 570kB/s]
Downloading: 100%|██████████| 418M/418M [00:04<00:00, 90.3MB/s] 
Downloading: 100%|██████████| 1.16k/1.16k [00:00<00:00, 505kB/s]
Downloading: 100%|██████████| 226k/226k [00:00<00:00, 5.47MB/s]
Downloading: 100%|██████████| 455k/455k [00:00<00:00, 10.0MB/s]
Downloading: 100%|██████████| 239/239 [00:00<00:00, 104kB/s]
Removing intermediate container 2407b608e382
 ---> f35b58edfef9
Step 5/6 : ENV CONFIG "config.yml"
 ---> Running in fdfaa1596467
Removing intermediate container fdfaa1596467
 ---> b7496b4daea7
Step 6/6 : ENTRYPOINT ["uvicorn", "--host", "0.0.0.0", "txtai.api:app"]
 ---> Running in 9a70ab36501c
Removing intermediate container 9a70ab36501c
 ---> a9901b24be98
Successfully built a9901b24be98
Successfully tagged dev.local/txtai:v1

5. Добавьте образ в кластер Knative (пропустите при установке в существующий кластер)

$ kind load docker-image --name knative dev.local/txtai:v1
Image: "dev.local/txtai:v1" with ID "sha256:a9901b24be986571178eff9471d875d6002c023dbc55353cd95171d832b7c851" not yet present on node "knative-control-plane", loading...

6. Создайте сервис Knative txtai

$ kn service create txtai --image dev.local/txtai:v1 --port 8000 --scale-max 1
Creating service 'txtai' in namespace 'default':
0.020s The Route is still working to reflect the latest desired specification.
  0.032s ...
  0.045s Configuration "txtai" is waiting for a Revision to become ready.
 11.200s ...
 11.227s Ingress has not yet been reconciled.
 11.273s Waiting for load balancer to be ready
 11.540s Ready to serve.
Service 'txtai' created to latest revision 'txtai-00001' is available at URL:
http://txtai.default.127.0.0.1.sslip.io

Протестируйте сервис

txtai имеет ряд доступных языковых привязок для работы с API. Для простоты мы будем взаимодействовать с txtai через cURL.

  1. Добавьте данные в индекс.
$ curl -XPOST "http://txtai.default.127.0.0.1.sslip.io/add" -H "Content-Type: application/json" \
--data-binary @- << EOF 
[{"id": 0, "text": "US tops 5 million confirmed virus cases"},
{"id": 1, "text": "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg"},
{"id": 2, "text": "Beijing mobilises invasion craft along coast as Taiwan tensions escalate"},
{"id": 3, "text": "The National Park Service warns against sacrificing slower friends in a bear attack"},
{"id": 4, "text": "Maine man wins \$1M from \$25 lottery ticket"},
{"id": 5, "text": "Make huge profits without work, earn up to $100,000 a day"}]
EOF

2. Индексные данные

$ curl "http://txtai.default.127.0.0.1.sslip.io/index"

3. Запустите поиск

$ curl "http://txtai.default.127.0.0.1.sslip.io/search?query=feel+good+story&limit=1"
[{
  "id":"4", 
  "text":"Maine man wins $1M from $25 lottery ticket",
  "score":0.08329004049301147
}]

Ладно, пора перевести дух. Мы какое-то время запускаем команды. Но мы только что построили индекс вложений и успешно выполнили поиск 🎉

Добавить постоянное хранилище

Одна важная особенность Knative — сервисы будут масштабироваться вверх и вниз. Если вы подождете минуту, а затем запустите следующее:

$ curl "http://txtai.default.127.0.0.1.sslip.io/count"
0

Счет теперь 0? Да. Из-за бездействия служба txtai была прекращена. Мы могли бы решить эту проблему, заставив экземпляр всегда запускаться, но это расточительно. Далее мы рассмотрим, как сохранить данные индекса.

  1. Чтобы все было локально, мы добавим локальный экземпляр S3 с LocalStack.
$ docker run -p 4566:4566 --rm -it localstack/localstack
2022-02-28 18:21:39,477 INFO supervisord started with pid 14
2022-02-28 18:21:40,481 INFO spawned: 'infra' with pid 20
2022-02-28 18:21:41,620 INFO success: infra entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
LocalStack version: 0.14.0
LocalStack build date: 2022-02-15
LocalStack build git hash: 8db17b52
Starting edge router (https port 4566)...
Ready.
[2022-02-28 18:21:41 +0000] [20] [INFO] Running on https://0.0.0.0:4566 (CTRL + C to quit)
2022-02-28T18:21:41.803:INFO:hypercorn.error: Running on https://0.0.0.0:4566 (CTRL + C to quit)

2. Затем мы отредактируем наш кластер Kubernetes, чтобы он мог взаимодействовать с LocalStack. Во-первых, давайте получим IP-адрес кластерной сети.

$ docker container ls -f "name=knative" 
CONTAINER ID   IMAGE
949b4e6bdda5   kindest/node:v1.22.4
$ docker inspect 949b4e6bdda5 | grep IPAddress
"IPAddress": "172.19.0.3",

В этом случае IP-адрес узла 172.19.0.1

$ kubectl edit cm coredns -n kube-system

Поднимает файл. Добавьте раздел сразу после строки с текстом «prometheus:9153.

hosts {
  172.19.0.1 localhost.localstack.cloud
  fallthrough
}

3. Перезапустите службу DNS.

$ kubectl rollout restart -n kube-system deployment/coredns
deployment.apps/coredns restarted

4. Теперь нам нужно отредактировать txtai config.yml.

# config.yml
writable: true
path: /tmp/index.tar.gz
cloud:
  provider: s3
  container: index
  key: ""
  secret: ""
  host: localhost.localstack.cloud
  port: 4566
embeddings:
  path: sentence-transformers/nli-mpnet-base-v2
  content: true

Этот раздел добавляет постоянство как в локальный экземпляр, так и синхронизирует индекс с LocalStack.

5. Пересоберите образ txtai

Примечание. Эта и следующие команды повторно запускают то, что мы делали раньше, чтобы перестроить службу txtai с новыми настройками.

$ docker build -t dev.local/txtai:v1 .
Sending build context to Docker daemon  3.072kB
Step 1/6 : ARG BASE_IMAGE=neuml/txtai-cpu
Step 2/6 : FROM $BASE_IMAGE
 ---> 894803a0dc04
Step 3/6 : COPY config.yml .
 ---> 5c88e1e649b0
Step 4/6 : RUN python -c "from txtai.api import API; API('config.yml', False)"
 ---> Running in dad9eb8ad91f
Downloading: 100%|██████████| 587/587 [00:00<00:00, 249kB/s]
Downloading: 100%|██████████| 418M/418M [00:04<00:00, 89.3MB/s] 
Downloading: 100%|██████████| 1.16k/1.16k [00:00<00:00, 582kB/s]
Downloading: 100%|██████████| 226k/226k [00:00<00:00, 6.90MB/s]
Downloading: 100%|██████████| 455k/455k [00:00<00:00, 9.82MB/s]
Downloading: 100%|██████████| 239/239 [00:00<00:00, 111kB/s]
Removing intermediate container dad9eb8ad91f
 ---> 91eb73e6756b
Step 5/6 : ENV CONFIG "config.yml"
 ---> Running in 49573ceb2fbf
Removing intermediate container 49573ceb2fbf
 ---> 95bb5499b335
Step 6/6 : ENTRYPOINT ["uvicorn", "--host", "0.0.0.0", "txtai.api:app"]
 ---> Running in fca01a3b9d52
Removing intermediate container fca01a3b9d52
 ---> fcf0db204af0
Successfully built fcf0db204af0
Successfully tagged dev.local/txtai:v1

6. Заменить образ в кластере Knative (пропустить при установке в существующий кластер)

$ kind load docker-image --name knative dev.local/txtai:v1
Image: "dev.local/txtai:v1" with ID "sha256:fcf0db204af0c38dc9ebd5779e077d1d9fb0a05ae64642d5a3595024220a270d" not yet present on node "knative-control-plane", loading...

7. Воссоздайте сервис Knative txtai

$ kn service create txtai --image dev.local/txtai:v1 --port 8000 --scale-max 1 --force
Replacing service 'txtai' in namespace 'default':
0.039s The Configuration is still working to reflect the latest desired specification.
 11.192s Traffic is not yet migrated to the latest revision.
 11.231s Ingress has not yet been reconciled.
 11.263s Waiting for load balancer to be ready
 11.537s Ready to serve.
Service 'txtai' replaced to latest revision 'txtai-00002' is available at URL:
http://txtai.default.127.0.0.1.sslip.io

8. Тестовый сервис

$ curl -XPOST "http://txtai.default.127.0.0.1.sslip.io/add" -H "Content-Type: application/json" \
--data-binary @- << EOF 
[{"id": 0, "text": "US tops 5 million confirmed virus cases"},
{"id": 1, "text": "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg"},
{"id": 2, "text": "Beijing mobilises invasion craft along coast as Taiwan tensions escalate"},
{"id": 3, "text": "The National Park Service warns against sacrificing slower friends in a bear attack"},
{"id": 4, "text": "Maine man wins \$1M from \$25 lottery ticket"},
{"id": 5, "text": "Make huge profits without work, earn up to $100,000 a day"}]
EOF
$ curl "http://txtai.default.127.0.0.1.sslip.io/index"
$ curl "http://txtai.default.127.0.0.1.sslip.io/search?query=feel+good+story&limit=1"
[{
  "id":"4", 
  "text":"Maine man wins $1M from $25 lottery ticket",
  "score":0.08329004049301147
}]

9. Дождитесь завершения службы.

$ kubectl get pod -l serving.knative.dev/service=txtai -w
NAME                                      READY   STATUS    RESTARTS   AGE
txtai-00002-deployment-745c77586f-ppd8c   2/2     Running   0          42s
txtai-00002-deployment-745c77586f-ppd8c   2/2     Terminating   0          92s

Теперь снова беги.

$ curl "http://txtai.default.127.0.0.1.sslip.io/search?query=feel+good+story&limit=1"
[{
  "id":"4", 
  "text":"Maine man wins $1M from $25 lottery ticket",
  "score":0.08329004049301147
}]

Индекс сохранялся при перезапуске, поскольку он синхронизировался с облачным хранилищем (в данном случае LocalStack, но это может быть любой облачный провайдер, подробнее здесь).

Подведение итогов

В этой статье показано, как в Kubernetes можно запустить экземпляр векторного поиска txtai без сервера. Хотя в этом примере рассматривается индекс внедрения, те же концепции можно применить для создания масштабируемой службы перевода, службы суммирования или службы рабочих процессов с помощью txtai.

☁️ — это предел того, как этот подход можно применить к другим проблемам!