Разговоры об открытой архитектуре RISC-V все чаще можно услышать вне круга разработчиков. Это логично: сейчас на ней разрабатываются не только сопроцессоры и процессоры для микроконтроллеров, серверов и суперкомпьютеров, но и процессоры для ноутбуков, планшетов и иной техники. 

Чем быстрее прогресс RISC-V, тем острее встает вопрос адаптации ПО под эту архитектуру. Например, инструментов для работы с микросервисами, без которых уже сложно представить современную разработку. Есть ли место контейнеризации в открытой архитектуре? Мы — команда совместной лаборатории «Технологии программирования» Санкт-Петербургского политехнического университета Петра Великого и YADRO — задались этим вопросом.

В статье вас ждет тестирование современных инструментов оркестрации — Kubernetes, K3s и Docker Swarm. Мы попробовали запустить их на микрокомпьютере Lichee Pi 4A и оценили их производительность.

В сравнении с x86 и ARM RISC-V — молодая архитектура. Она зародилась только в 2010 году, в Калифорнийском университете Беркли, США. А более-менее неакадемическое ее развитие стартовало в 2015 году, вместе в  образованием глобальной некоммерческой ассоциации RISC-V International. Ввиду этой молодости на архитектуру портированы далеко не все приложения. Но над этим работают порядка 400 компаний, входящих в международный Альянс RISC-V, включая российские организации. 

Внести свою лепту решили и мы. Задача проекта — изучить пригодность компьютеров с процессорной архитектурой RISC-V для запуска микросервисов и работы с ними. Мы хотели узнать, что именно уже портировано и насколько хорошо оптимизированы портированные программы. 

Именно микросервисные приложения мы выбрали по двум причинам:

  • Работа с микросервисами требует определенный стек приложений. Как следствие, при настройке окружения мы заодно проверим портированность широкого круга программ.

  • Микросервисы сейчас крайне востребованы, поэтому портирование приложений для их разработки — крайне актуальная задача.

Для запуска и корректной работы микросервисов нам потребовалось изучить портированность и оптимизированность контейнеризаторов и оркестраторов под RISC-V. Помимо этого, мы проанализировали компиляторы под самые популярные языки программирования и некоторые фреймворки для RISC-V.

Стабильная работа микросервисных приложений невозможна без оркестраторов. В этой статье мы сосредоточимся именно на них, ведь оркестратор — важнейший компонент экосистемы микросервисов. Без него невозможно эффективное развертывание и управление сервисами. Другие инструменты, необходимые для работы микросервисной архитектуры на RISC-V, мы рассмотрим в следующих публикациях.

Сборка стенда на RISC-V — на базе Lichee Pi 4A 

Центральной фигурой нашего стенда стал микрокомпьютер Lichee Pi 4A (плата-носитель LPi4A и процессорный модуль LM4A SOM). Кроме него, у нас были: 

  • антенна WiFi/Bluetooth,

  • кулер CAIZHU-FAN DC 5v с термоинтерфейсом,

  • кабель USB type 2.0\USB type C,

  • сенсорный жидкокристаллический ЖК-дисплей на 10.1 дюймов,

  • JTAG-отладчик,

  • стойка для дисплея,

  • два кабеля FPC-FFC для подключения дисплея.

Изображение выглядит как текст, диаграмма, План, Шрифт  Контент, сгенерированный ИИ, может содержать ошибки.
Спецификация платы Lichee Pi 4A

На обратной стороне платы есть ряд портов:

Изображение выглядит как текст, диаграмма, План, схематичный  Контент, сгенерированный ИИ, может содержать ошибки.
Обратная сторона платы LPi4A

Сборка

Подключаем плату: нужно провести коммутацию плат LM4A SOM и LPi4A, соединив в порт — SODIMM Connector.

Процессор TH1520 находится на LM4A SOM, поэтому кулер CAIZHU-FAN мы разместим на нем. Кулер подключается к 2pin разъему GPIO10_PWM1 на плате LPi4A.

Также подключим к плате WiFi-антенну через Antenna connector. Фиксируем плату на стенде с помощью винтовых разъемных крепежных соединений, входящих в комплект.

Плата с кулером и подключенной WiFi-антенной
Плата с кулером и подключенной WiFi-антенной

Теперь подключим дисплей. У нас есть два FPC-кабеля type А: позолоченный на 31 пин для подключения через MIPI DSI и белый на 6 пинов для передачи по I2C. А еще — переходник LcdTP — LPi4a.

Слева направо: шлейф для подключения по MIPI DSI, шлейф для подключения по I2C и переходник LCDTP — LPi4a
Слева направо: шлейф для подключения по MIPI DSI, шлейф для подключения по I2C и переходник LCDTP — LPi4a

Одной стороной шлейф на 6 пинов подключается в порт Touching connector на плате, а другой — в переходник, в разъем LPi4A. Разъем LCDTP подключается к дисплею, как показано на рисунке ниже.

Изображение выглядит как текст, снимок экрана, диаграмма, Прямоугольник  Контент, сгенерированный ИИ, может содержать ошибки.
Схема подключения платы к дисплею

Кабель на 31 пин подключается одной стороной в порт MIPI DSI, другой — в аналогичный порт на дисплее. Дисплей располагается на креплении вертикально, входя в пазы.

Запуск

В комплекте поставляется блок питания SD1202000. Его нужно подключить в порт 12V DC Power. Система автоматически запускает BIOS, когда питание поступает на плату. Подключение к сети — лишь один из способов питания платы. Альтернативная возможность подачи электроэнергии — через порт USB-C на верхней части платы. 

Есть также возможность подать питание на плату с помощью технологии Power over Ethernet, или PoE (если есть соответствующий аппаратный модуль — например, SDaPo PM0503T 2225). Если плата подключена именно так, то нажатием кнопки BOOT ее можно подключить к компьютеру и, например, переустановить систему.

Устанавливаем операционную систему

При выборе ОС мы опирались на следующие критерии:

  • Поддержка архитектуры RISC-V.

  • Возможность контейнеризации и многопоточности — ключевых для запуска микросервисного ПО.

  • Открытость системы. Мы не могли предсказать результаты экспериментов в рамках проекта, поэтому заложили необходимую гибкость в изменении конфигурации стенда. 

  • Распространенность и перспективность. Микросервисные приложения запускаются на различных процессорных архитектурах. Выбрав востребованную ОС, мы обеспечиваем равные условия на системном уровне, что повышает точность сравнительного анализа процессорных архитектур.

Сравнительная таблица с популярными ОС:

Поддержка RISC-V

Контейнеризация

Открытость

Популярность среди пользователей

Распространенность и перспективность

Windows

нет

да

нет

да

да

Linux

да

да

да

да

да

Android

да

нет

да

да

нет

FreeBSD

да

да

да

нет

да

Оптимальным выбором стал Linux. Но у Linux, в свою очередь, есть множество дистрибутивов, из которых тоже нужно выбрать. Мы сразу рассматривали шорт-лист из трех дистрибутивов, которые сравнили между собой: 

Поддержка RISC-V

Рекомендован производителем Lichee Pi 4A

Есть инструкция по установке на Lichee Pi 4A

Debian

да

да 

да 

Ubuntu

да

нет

нет

Fedora

да

нет

да 

Список ОС, портированных на RISC-V, можно посмотреть по ссылке. Но, помимо поддержки RISC-V, нужна еще поддержка Lichee PI 4A. Так как из-за открытости и модульности архитектуры ее реализация может отличаться на разных процессорах. 

В итоге мы решили установить последнюю версию Linux Debian, тем более эту ОС рекомендует производитель Lichee PI 4A. Подробную инструкцию по установке ОС можно найти здесь

На плате от официальных поставщиков ОС Debian уже может быть предустановлена. Но сами производители заявляют, что с ее работой могут возникнуть проблемы. Поэтому всегда лучше переустановить систему.

По ссылке устанавливаем следующие архивы:

  • 20240111/LPI4A_FULL_20240111.zip — образ самой системы,

  • burn_tools_support_bigimage.zip — средство установки (fastboot).

Для установки Debian необходим ПК с Linux или Windows. Для установки с Windows требуются дополнительные драйверы, поэтому оптимальным решением будет установка ОС с Linux. Весь процесс установки занимает в среднем 10-15 минут. Перед соединением платы с компьютером необходимо подготовить образ ОС. Для этого выполним несколько шагов.

Сначала распакуем архив с образом системы. После этого нужно скопировать в папку с распакованным архивом fastboot.

Содержимое созданной папки image
Содержимое созданной папки image

Теперь необходимо запустить на плате режим загрузки. Для этого выключаем плату из сети, зажимаем кнопку BOOT и в этот момент коммутируем с ПК через USB-C. Чтобы проверить подключение, введем на ПК команду:

lsusb 

В списке устройств должно быть:

ID 2345:7654 T-HEAD USB download gadget

Затем выполняем разметку в памяти следующими командами:

sudo ./fastboot flash ram ./u-boot-with-spl-lpi4a-16g.bin
sudo ./fastboot reboot
sleep 1

Выполняем прошивку для загрузчика uboot, файловой системы загрузки boot и корневой файловой системы root: 

sudo ./fastboot flash uboot ./u-boot-with-spl-lpi4a-16g.bin
sudo ./fastboot flash boot ./boot_sing.ext4
sudo ./fastboot flash root ./rootfs-sing.ext4

В результате мы должны увидеть:

Finished. Total time: 281.671s

Теперь можно запускать плату, включив ее в сеть. Ваш дисплей должен показать рабочий стол, как на фото ниже, или подобный ему — версии ОС могут различаться.

Изображение выглядит как электроника, ноутбук, Электронное устройство, в помещении  Контент, сгенерированный ИИ, может содержать ошибки.
Рабочий стол стоковой операционной системы Debian

При обычной установке видеовыход будет направлен на HDMI, а MIPI DSI не будет восприниматься системой. Чтобы это исправить, нужно скачать файл extlinux.conf из репозитория и заменить им существующий в /boot/extlinux/. Он отвечает за описание видеовыходов и ставит один из них по умолчанию.

Запуск микросервисного приложения

Для тестирования работы микросервисов на платформе RISC-V мы выбрали приложение «Микрокалькулятор». Во многом из-за простоты разработки и запуска, а также из-за возможности высокой нагруженности — имитации более 1000 пользователей. Исходный код можно найти в репозитории GitHub

Основной функционал микрокалькулятора — нахождение дисперсии последовательности чисел. Для подсчета дисперсии (disp) используются операции сложения (add), вычитания (sub), умножения (mult), деления (div) и возведения в степень (pow). И для каждой операции выделен свой микросервис. Запросы к микросервисам идут от клиентского приложения (parser). 

Изображение выглядит как снимок экрана, дизайн  Контент, сгенерированный ИИ, может содержать ошибки.
Структура приложения

 Приложение написано с помощью различных языков программирования:

  • на Go — disp, add, sub, mult,

  • на Python — div, pow,

  • на Java — parser.

Микросервисы общаются между собой с помощью HTTP-запросов и ответов в формате JSON. Для Python используем Flask, для Java — Spring, для Go — библиотеку net/http.

Для запуска приложения будем использовать три способа:

  • в обычных процессах, используя компиляторы и интерпретаторы,

  • в контейнерах,

  • с помощью системы оркестрации. 

Программы на языке программирования Go компилируются с помощью команды go build main.go.

Микросервисы на Python запускаются интерпретатором python3 с помощью команды python3 main.py.

Для удобства мы написали bash-скрипт для запуска всех микросервисов (пока без клиента):

#!/bin/bash

PATH_TO_SERVICES="<path_to_microcalc>/services"

$PATH_TO_SERVICES/add/main&
$PATH_TO_SERVICES/sub/main&
$PATH_TO_SERVICES/mult/main&
$PATH_TO_SERVICES/disp/main&
python3 $PATH_TO_SERVICES/div/main.py&
python3 $PATH_TO_SERVICES/pow/main.py&

Для быстрого завершения процессов у нас также есть отдельно созданный bash-скрипт:

#!/bin/bash
kill -9 $(lsof -t -i :3000)
kill -9 $(lsof -t -i :3001)
kill -9 $(lsof -t -i :4000)
kill -9 $(lsof -t -i :5000)
kill -9 $(lsof -t -i :8000)
kill -9 $(lsof -t -i :18080)

Для компиляции кода на Java мы использовали OpenJDK 21. После компиляции исходного кода мы получаем файл .jar. Для его запуска используем написанный нами bash-скрипт:

#!/bin/bash
PATH_TO_JAR="<path_to_microcalc>/services/parser/target/parser-1.0-SNAPSHOT.jar"

echo "integer test"
java -jar "$PATH_TO_JAR" < test_int.txt

echo "float .1f test"
java -jar "$PATH_TO_JAR" < test_1f.txt

echo "float .5f test"
java -jar "$PATH_TO_JAR" < test_5f.txt

echo "double test"
java -jar "$PATH_TO_JAR" < test_double.txt

Файлы test_* содержат последовательность из 1000 чисел, для которых считается дисперсия, а также команды для клиента. Примеры чисел:

  • test_int.txt → 0 1 2.

  • test_1f.txt → 0.1 0.5 -1.1.

  • test_5f.txt → 0.80383 1.92171 1.19699.

  • test_double → 0.952269308 -2.193889724 1.706063348.

Выбираем оркестраторы для оценки их работы на RISC-V

Чем больше микросервисов, тем больше контейнеров — как следствие, усложняются процессы логирования и мониторинга их работы. Здесь на помощь приходят системы оркестрации, которые помогают: 

  • обеспечивать контейнеры ресурсами,

  • мониторить работу контейнеров,

  • конфигурировать сеть,

  • управлять жизненным циклом контейнеров,

  • перезапускать или останавливать контейнеры.

Все это обеспечивает удобный и автоматизированный процесс работы с множеством контейнеров.

Наиболее используемый и функциональный оркестратор — это Kubernetes (K8s). На момент публикации статьи K8s официально не поддерживается на RISC-V, но существует его легковесный аналог K3s, который адаптирован под открытую архитектуру. Не забыли мы и про Docker Swarm, который, в свою очередь, также поддерживается на RISC-V. 

Именно эти оркестраторы мы рассматриваем в статье.

Работаем с Kubernetes (K8s)

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

Существует несколько способов запуска K8s на нашем микрокомпьютере. Например, можно:

  • использовать makefile в основном каталоге репозитория,

  • использовать скрипты из папки hack основного каталога,

  • вручную скомпилировать исходный код, используя компилятор Go,

  • запустить старую версию K8s для RISC-V.

Makefile

Первый и самый простой способ — запустить make, чтобы он сам собрал K8s из исходного кода.

И тут мы сразу сталкиваемся с проблемой: скрипты для компиляции и сборки не содержат RISC-V в списке поддерживаемых архитектур. А еще эти скрипты используют ПО, которое не поддерживается на RISC-V, например kube-cross. 

Делаем вывод, что сборка через make не адаптирована под RISC-V, поэтому установить K8s таким способом не получится.

Hack

В папке hack в основном каталоге Kubernetes есть скрипт local-up-cluster.sh.  Его задача — поднять кластер K8s на локальной машине. Также он компилирует исходный код в бинарные файлы модулей оркестратора, если их нет. 

Но при запуске возникла та же проблема: в списке целевых архитектур нет RISC-V. При добавлении архитектуры скрипт завершил работу, и бинарные файлы действительно скомпилировались, но работать не стали.

Изображение выглядит как текст, снимок экрана, Шрифт  Контент, сгенерированный ИИ, может содержать ошибки.
Ошибка при запуске тестового приложения на K8s

Возможно, требуется настройка конфигурации, сети или иных параметров для запуска. Но мы решили отказаться от дальнейшей работы в этом направлении, потому она, очевидно, заняла бы очень много времени. Это же касается ручного запуска скомпилированных бинарных файлов.

Запуск версии Kubernetes 1.16.0

Мы нашли публикацию про тестирование Docker и Kubernetes на ARM и RISC-V. В ней авторы рассказали о запуске на RISC-V старой версии Kubernetes 1.16.0 и оставили ссылку на пакет с ней. При этом авторы статьи предупредили, что могут возникнуть проблемы с новыми версиями Docker. Так и получилось: один из необходимых модулей K8s (kubelet) отказался запускаться на нашей RISC-V-машине.

Изображение выглядит как текст, снимок экрана, дизайн  Контент, сгенерированный ИИ, может содержать ошибки.
Ошибка при инициализации образа для K8s

Как выяснилось, Docker с версии 20.10 использует cgroups v2. Cgroups — это группа процессов ядра Linux для управления ресурсами (используется для контейнеризации). И для запуска kubelet версии 1.16.0 необходим cgroup v1. На данный момент механизмы загрузчика системы на RISC-V не позволяют вручную изменить версию cgroups.

Таким образом, ни один из способов запуска K8s не увенчался успехом, поэтому мы перешли к тестированию других оркестраторов. 

Работаем с K3s

K3s — это полностью совместимый и официальный дистрибутив Kubernetes, предназначенный для упрощенной работы в средах с ограниченными ресурсами. Он включает в себя несколько особенностей:

  • Распространяется как один бинарный файл или минимальный контейнерный образ.

  • По умолчанию использует легковесное хранилище SQLite3, также поддерживает etcd3, MySQL и PostgreSQL.

  • Позволяет выполнять упрощенный запуск с автоматизацией настройки параметров.

  • Безопасен по умолчанию благодаря предустановленным параметрам для легковесных окружений.

  • Объединяет все компоненты контрольной плоскости Kubernetes в один процесс, упрощая управление кластером.

  • Минимизирует внешние зависимости (необходимы только современное ядро и поддержка cgroup). 

  • Включает все необходимые зависимости для развертывания кластера «из коробки»: containerd/cri-docker, Flannel, CoreDNS, Traefik, ServiceLB, Kube-router и другие.

Среди преимуществ K3s можно выделить компактность и легковесность, автоматизированное управление, гибкость развертывания (можно использовать как на одиночном узле, так и в отказоустойчивых кластерах), совместимость с API Kubernetes.

Так как стандартный Kubernetes не поддерживается на RISC-V, K3s может стать его реальной альтернативой. Адаптация K3s к RISC-V потребовала переноса и оптимизации нескольких зависимостей, включая runc, CoreDns, Helm и Traefik. Эти изменения внесены в официальные репозитории, что подтверждает зрелость проекта для использования на открытой платформе.

Существует несколько возможных вариантов развертывания K3s:

  • Кластер из одной ноды — самый простой вариант установки, но без отказоустойчивости.

Изображение выглядит как текст, снимок экрана, круг, Шрифт  Контент, сгенерированный ИИ, может содержать ошибки.
Кластер из одного узла
  • Один кластер с несколькими агентами — серверный узел с возможностью подключения нескольких агентов.

Изображение выглядит как снимок экрана, текст, Шрифт, число  Контент, сгенерированный ИИ, может содержать ошибки.
Один кластер с несколькими агентами
  • Высокодоступный кластер с внешней БД — отказоустойчивый кластер, где используются несколько серверных узлов и внешнее хранилище данных, например etcd, MySQL, PostgreSQL.

Изображение выглядит как снимок экрана, текст, Шрифт, число  Контент, сгенерированный ИИ, может содержать ошибки.
Высокодоступный кластер с внешней БД

Тестируем K3s на RISC-V

Чтобы оценить возможности и производительность K3s на платформе RISC-V (Lichee Pi 4A), мы провели комплексное тестирование. Оно охватило следующие аспекты:

  • Производительность CPU и памяти. 

  • Контейнеризация (работа Docker и K3s). 

  • Реальный сценарий с запуском микросервисного приложения microcalc.

Производительность CPU и памяти

Для оценки производительности центрального процессора и оперативной памяти мы использовали утилиту stress-ng. Она позволяет задать различные методы нагрузки и выдает метрики, характеризующие работу системы. 

Тестирование процессора

Команда:

stress-ng --cpu 4 --cpu-method matrixprod --metrics-brief --timeout 60s

Параметры:

  • --cpu 4 — задействованы четыре ядра.  

  • --cpu-method matrixprod — нагрузка через матричные вычисления.

  • --timeout 60s — продолжительность теста (60 секунд). 

  • --metrisc-brief — вывод краткой статистики. 

Результаты:

Параметр

Значение

Общее число операций (bogo ops)

2.996

Время выполнения (real time, сек)

60.19

Пользовательское время (usr time)

239.14

Системное время (sys time)

0.15

Производительность (по real time)

49.77 bogo ops/s

Производительность (по usr + sys)

12.52 bogo ops/s

Производительность процессора приемлема, но не максимальна. Скорее всего, используемый процессор на RISC-V (T-Head) не оптимизирован под интенсивные матричные вычисления. 

Тестирование памяти

Команда:

stress-ng --vm 2 --vm-bytes 1G --vm-method all --metrics-brief --timeout 60s

Параметры:

  • --vm 2 — два параллельных процесса. 

  • --vm-bytes 1G — по 1 ГБ памяти на каждый процесс. 

  • --vm-method all — задействованы все методы нагрузки. 

Результаты:

Параметр

Значение 

Общее число операций (bogo ops)

1.404.904

Время выполнения (real time, сек)

60.46

Пользовательское время (usr time)

91.48

Системное время (sys time)

28.91

Производительность (по real time)

23.235.59 bogo ops/s

Производительность (по usr + sys)

11.670.22 bogo ops/s

Скорость работы с памятью высокая. Это говорит о хорошем взаимодействии системы с RAM и эффективной работе механизма управления памятью на Lichee Pi 4A. 

Контейнеризация и работа K3s

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

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

Тестирование контейнера busybox

Команда:

sudo docker run --rm -it busybox sh -c "dd if=/dev/zero of=/dev/null bs=1M count=10000"

Результат:

  • Контейнер успешно запущен. 

  • Скорость записи: 6.7 Гб/с при передаче 10 Гб данных. 

Это говорит о высокой пропускной способности ввода/вывода в контейнерной среде. 

Развертывание пода и деплоймента

Создание пода:

sudo kubectl run test-pod --image=busybox -- sleep 3600

Проверка:

sudo kubectl get pods

Создание деплоймента:

sudo kubectl create deployment nginx --image=nginx

Открытие доступа через NodePort:

sudo kubectl expose deployment nginx --type=NodePort --port=80

Система K3s на RISC-V стабильно разворачивает как одиночные поды, так и полноценные деплойменты с доступом из внешней сети. Это подтверждает готовность платформы к использованию в легковесных микросервисных архитектурах. 

Нагрузка сети 

Для оценки сетевой производительности мы провели серию тестов с помощью утилиты iperf3. Выполняли их между устройствами Lichee Pi 4A — Agent и Server.

Запустили сервер с помощью команды iperf3 -s и клиент — iperf3 -c <IP сервера> -t 30

Теперь посмотрим на результаты тестирования.

Серверный режим (прием данных):

[ ID] Interval           Transfer     Bitrate

[  5]   0.00-30.05  sec   133 MBytes  37.2 Mbits/sec                  

Клиентский режим (передача данных):

[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  5.29 MBytes  44.4 Mbits/sec    
[  5]   1.00-2.00   sec  4.35 MBytes  36.5 Mbits/sec    
[  5]   2.00-3.00   sec  4.97 MBytes  41.7 Mbits/sec    
[  5]   3.00-4.00   sec  3.39 MBytes  28.4 Mbits/sec    
[  5]   4.00-5.00   sec  4.75 MBytes  39.9 Mbits/sec    
[  5]   5.00-6.00   sec  4.87 MBytes  40.9 Mbits/sec    
[  5]   6.00-7.00   sec  5.00 MBytes  41.9 Mbits/sec    
[  5]   7.00-8.00   sec  3.75 MBytes  31.5 Mbits/sec    
[  5]   8.00-9.00   sec  5.00 MBytes  41.9 Mbits/sec    
[  5]   9.00-10.00  sec  3.75 MBytes  31.5 Mbits/sec    
[  5]  10.00-11.00  sec  5.00 MBytes  41.9 Mbits/sec    
...
[  5]  28.00-29.00  sec  4.15 MBytes  34.8 Mbits/sec    
[  5]  29.00-30.00  sec  4.70 MBytes  39.4 Mbits/sec    
[  5]  30.00-30.05  sec   260 KBytes  39.3 Mbits/sec   

По результатам тестирования средняя скорость передачи данных составила 37.2 Мбит/с в режиме сервера и достигла 44.4 Мбит/с в режиме клиента. Скорость приемлема для Wi-Fi, но не высока для гигабитной сети. Также не было потерь, что указывает на хорошее качество связи.

Запускаем микросервисное приложение в K3s

Пришло время запустить наше приложение microcalc, состоящее из шести модулей (add, sub, mult, div, pow, disp). 

Для каждого сервиса создаем два файла:

  • {service}-deployment.yaml — описывает поды и их реплики. 

  • {service}-service.yaml — настраивает доступ к сервису. 

Пример файлов для сервиса add:

add-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: add
spec:
  replicas: 1
  selector:
    matchLabels:
      app: add
  template:
    metadata:
      labels:
        app: add
    spec:
      containers:
      - name: add
        image: nctortue/add_service:latest
        resources:
          limits:
            memory: "64Mi"
            cpu: "100m"

add-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: add
spec:
  selector:
    app: add
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3001  # Порт, который слушает контейнер

Запускаем микросервисы:

kubectl apply -f 

Проверяем состояние подов:

Изображение выглядит как снимок экрана, текст, дисплей  Контент, сгенерированный ИИ, может содержать ошибки.

Все сервисы успешно запустились в статусе Running. 

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

Усредненные  результаты представлены в таблице: 

Сервис

Память (MiB)

Потоки (threads)

CPU%

Add

5.4

5

N/A

Sub

4.9

6

N/A

Mult

4.7

5

N/A

Div

24.2

1

N/A

Pow

24.2

1

N/A

Disp

5.2

5

N/A


Какие основные наблюдения можем сделать. По памяти: Python-сервисы (div, pow) потребляют ~24 MiB (из-за интерпретатора Python), а сервисы на Go используют 4-6 MiB. По потокам: Go-сервисы (add, mult) используют 5-6 потоков, а Python-сервисы только один. 

Замерить CPU в итоге не удалось. Есть несколько причин: 

  • K3s не смог развернуть metrics-server (нет готовых образов под RISC-V). 

  • Стандартные инструменты (kubectl top pods) требуют metrics-api, который недоступен. 

  • Проблемы с /proc/stat: скрипт мониторинга использует /proc/stat для расчета процента утилизации CPU, но доступ к этим данным ограничен. 

Приложение не нагружает систему: потребление памяти — менее 30 MiB на сервис. По косвенным данным можно определить, что CPU-нагрузка тоже минимальна. Также K3s корректно работает с микросервисами. Остается одна сложность: нет инструментов мониторинга — например, не хватает Prometeus или Grafana. 

Работаем с Docker

Docker Swarm — встроенный инструмент в Docker для управления кластерами контейнеров. Оркестратор работает по модели manager–worker, где узлы делятся на две роли:

  • Manager-узлы — управляют кластером и распределяют задачи.

  • Worker-узлы — выполняют различные операции над запущенными контейнерами.

Для тех, кто мало знаком с Docker Swarm

Manager-узел включает в себя:

  • Raft Consensus Algorithm — обеспечивает согласованность состояния данных (журнала команд (log) и применение команд) во всех узлах. 

  • Orchestration Manager — отслеживает сервисы.

  • Scheduler — распределяет задачи между worker-узлами.

  • Dispatcher — отправляет задачи и собирает обратную связь.

  • API Endpoint — обрабатывает команды.

  • Leader Election — выбирает лидера для оркестрации.

Worker-узел включает в себя:

  • Task Executor — управляет контейнерами.

  • Container Runtime — запускает контейнеры.

  • Agent — отчитывается о статусе задач.

Также термины, которые нам понадобятся:

  • Node — виртуальные машины, на которых установлен Docker.

  • Stack — сервисы, которые связаны между собой.

  • Service — описание создаваемых контейнеров.

  • Task — созданный по описанию в Service контейнер.

Достоинства Docker Swarm: 

  • Простая установка, которая подойдет тем, кто только начинает знакомство с оркестрацией контейнеров.

  • Небольшой вес и высокая производительность.

  • Это нативный оркестратор для Docker, который полностью интегрирован с Docker Engine. Не требует дополнительных компонентов, кроме самого Docker Engine.

  • Это проект с открытым исходным кодом, который можно адаптировать и модифицировать под свои нужды.

  • Поддерживает Docker Compose и другие инструменты для разработки и развертывания приложений.

Установка Docker на Lichee Pi 4А с архитектурой RISC-V ничем не отличается от других платформ. 

Используем команду sudo apt install docker, проверяем командой docker --version.

Создание кластера также не вызвало проблем. Мы инициализировали Swarm (docker swarm init) и проверили его состояние (docker info). Результат — Swarm: active

Получили состояние текущих узлов командой docker node ls. Добавить новые узлы можно с помощью docker swarm join --token <TOKEN> <HOST:PORT> (эта команда была после инициализации). 

Если у вас нет доступа, то следует выполнить команду docker swarm join-token worker на manager-узле для получения информации о команде подключения.

Оцениваем производительность Docker Swarm на платформе RISC-V

Как и в случае с K3s, мы провели тесты, направленные на оценку нагрузки на CPU, память и общую эффективность выполнения приложений. Использовали как синтетические тесты (через stress-ng), так и реальные сценарии с запуском микросервисного приложения microcalc. 

Тестирование с использованием stress-ng

Для симуляции нагрузки использовали инструмент stress-ng, позволяющий эмулировать интенсивную нагрузку на работу процессора и память. Тесты проводились в рамках сервиса, запущенного через Docker Swarm:

docker service create --name stress-test \

  --replicas 1 \

  --detach=false \

  stress-ng-matrix

Параметры: 

  • --replicas 1 — запуск одного контейнера.

  • --detach=false — отображение логов консоли.

Нагрузка на процессор:

Параметр

Значение

Общее число операций (bogo ops)

188

Время выполнения (real time, сек)

60.70

Пользовательское время (usr time)

221.87

Системное время (sys time)

0.70

Производительность (по real time)

3.10 bogo ops/s

Производительность (по usr + sys)

0.84 bogo ops/s

Результат показывает низкий уровень производительности при нагрузке на процессор. Это может быть связано с архитектурными ограничениями платформы или отсутствием оптимизации. 

Нагрузка на память:

Параметр

Значение 

Общее число операций (bogo ops)

426.122

Время выполнения (real time, сек)

60.68

Пользовательское время (usr time)

71.20

Системное время (sys time)

19.19

Производительность (по real time)

7022.10 bogo ops/s

Производительность (по usr + sys)

4714.15 bogo ops/s

Память, в свою очередь, показывает высокую интенсивность использования и достаточно высокую производительность. А значит, контейнеры хорошо работают с подсистемой памяти. 

Запускаем микросервисное приложение в Docker Swarm

Развертываем наш микрокалькулятор. 

Инициализация Swarm:

docker swarm init

Запуск микросервисов через docker-compose.yml:

version: '3.8'
services:
 add:
  image: nctortue/add_service:latest
   deploy:
    replicas: 1
    resources:
     limits:
       cpus: '0.1'
       memory: 64M

   ports:
    - "3001:3001"

 sub:
  image: nctortue/sub_service:latest
  deploy:
   replicas: 1
   resources:
    limits:
     cpus: '0.1'
     memory: 64M

    ports:
    - "8000:8000"

  # Далее аналогично для mult, div, pow, disp...

Запускаем стек:

docker stack deploy -c docker-compose.yml microcalc

Для оценки нагрузки мы использовали адаптированный скрипт мониторинга, аналогичный тому, что мы применили в тестировании K3s:

Сервис

Память (MiB)

Потоки (threads)

CPU%

Add

5.1

5

0.5

Sub

4.9

6

0.3

Mult

4.7

5

0.4

Div

35.0

1

4.8

Pow

34.9

1

6.1

Disp

5.2

5

0.2

Приложение аналогично не нагружает систему, но потребление памяти сервисов на Python стало немного больше. CPU-нагрузка минимальна.

Подводим итоги

Поскольку классический Kubernetes выпал еще на старте (он не был полноценно портирован на RISC-V), в итоге мы можем сравнить только K3s и Docker Swarm. Оба оркестратора успешно запускают микросервисы на RISC-V (Lichee Pi 4A), но с разными компромиссами.

Из-за небольшой нагрузки сложно выявить явного лидера. Docker Swarm проще в развертывании, а K3s — более гибкий в работе, так как поддерживает Kubernetes-инструменты.

В синтетических тестах (stress-ng) K3s показал лучшую производительность на матричных вычислениях — он обошел показатели Docker Swarm примерно в 16 раз. В цифрах: 2.996 bogo ops/s (K3s) против 188 bogo ops/s (Docker Swarm). Возможно, это связано с оптимизацией Kubernetes или меньшими накладными расходами: K3s потребляет меньше ресурсов на фоновые процессы, такие как управление кластером и сетью, что важно для маломощных устройств.

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

Docker Swarm, во много благодаря своей простоте, может быть удобен для небольших проектов. Однако не стоит забывать, что архитектура RISC-V продолжает развиваться, и, возможно, в скором времени Альянс RISC-V порадует нас портированной версией полноценного К8s.

Мы хотим продолжить оценивать портируемость современного ПО для микросервисов на RISC-V. Если вас интересуют конкретные решения, напишите об этом в комментариях! 

Комментарии (0)