Что это и когда помогает
Машина для посылок — любая машина с Docker, которая исполняет тетради посылок соревнований вашего сервера Colloq: арендованная VM на Vast, сервер вуза, рабочая станция лаборатории. Машина сама приходит к серверу по HTTPS, берёт посылку из общей очереди, исполняет тетрадь в той же одноразовой песочнице без сети, что и сервер, и отправляет результат обратно. Оценивает его сервер: материалы, очередь, скрытые ответы и метрика остаются у него. На машине работает тот же образ colloq-server, только в режиме runner — без сервера, без туннеля relay и без базы, один контейнер агента colloq-runner.
Машины помогают в трёх случаях:
- Большое соревнование. Сто участников отправляют тетради в последние минуты, а мест на сервере — несколько. Машины добавляют к ним свои места; очередь остаётся одна и честная по людям, как и без машин (см. очередь).
- Тетради на видеокарте, которые сервер не потянет. У сервера нет карт или их мало: GPU-посылки возьмут машины с картами, например VM на Vast с двумя RTX 4090.
- Небольшой сервер. VM вуза на 8 vCPU держит занятие, а не очередь посылок. Переведите «Тетради на этом сервере» в «только метрики» — и сервер будет только оценивать ответы, которые прислали машины, а его ядра останутся занятию (см. ниже). Посылке можно дать и больше ядер, чем у сервера (до 32): такую тетрадь возьмёт машина, где ядер хватает (см. как исполняется посылка).
Для участника почти ничего не меняется: та же страница соревнования, те же пределы времени и памяти, те же исходы. Пока его посылку не может взять ни одно место — машины ещё качают образ среды или данные, или подходящей машины нет на связи, — вместо места в очереди он видит «готовится», а пока сервер считает метрику — «оценивается».
Машины подключает и видит только владелец сервера: в панели они в блоке «Машины» вкладки «Ресурсы». Машина, которой владелец открыл занятия, держит ещё и ядра классов — см. занятия на машинах.
Что остаётся на сервере и что видит машина
| Где | Что |
|---|---|
| Только на сервере | База данных, участники и их ключи входа, скрытые ответы (solution.csv), код метрики, счёт и лидерборды, очередь и её порядок. Метрику каждой посылки сервер считает сам, во втором одноразовом контейнере, — и тогда, когда тетрадь исполнила машина. |
| Машине | Тетради посылок, которые она исполняет; открытые данные соревнования; готовые наборы «своих пакетов» его участников — по ним видно, какие пакеты выбрали соперники. Образ среды машина скачивает из реестра сама, по дайджесту, который назвал сервер. |
| Только доверенной машине | Скрытый тест — входные данные, которые тетрадь читает при проверке вместо открытого примера (см. скрытый тест). По умолчанию машина не доверенная. |
| Машине, которая принимает занятия | Всё, что есть у её классов: ячейки и вывод тетрадей класса и личных тетрадей, копия папки занятия, терминал, память ядер. Скрытый тест такой машине не дают (см. занятия на машинах). |
| Никогда | Скрытые ответы, код метрики, счёт, имена участников, их почта и ключи входа, база данных, ключ подписи ссылок, токен relay. |
Обратно машина присылает файл ответа submission.csv, исполненную тетрадь и отчёт о прогоне. Сервер принимает их как данные участника: проверяет размеры и суммы, раскладывает файлы сам, а тексты для участника — в том числе короткие, при скрытом тесте, — пишет своими словами. Границы подробнее — в разделе «Изоляция и ограничения».
Метрика соревнования с видеокартой считается на сервере в лёгком образе kaggle-base (numpy, pandas, scikit-learn, scipy), а не в GPU-образе соревнования на 10–13 ГБ: метрике нужны две таблицы, а не карта. Образ закрепляется за соревнованием при первой его оценке и дальше не меняется — ни для новых посылок, ни при пересчёте, ни если карту в соревновании потом выключить. Перевести метрику на текущий kaggle-base можно только явно: в редакторе соревнования с картой — строкой «Образ метрики» в разделе «Метрика», а в пульте — кнопкой «Перевести метрику на текущий kaggle-base» у посылки, метрика которой упала, потому что закреплённого образа на сервере больше нет. Тогда пересчитываются все посылки соревнования, а тетради заново не выполняются. Поэтому держите kaggle-base готовым на сервере до первой оценки (KERNEL_PRELOAD=base,kaggle-base): соревнование, первая метрика которого посчиталась без него, остаётся на закреплённых образах самих посылок, как до этой версии; проверка бейслайна на процессоре, до того как включили карту, ничего не закрепляет. Метрика, которая импортирует torch, здесь упадёт как код метрики — «УПАЛА МЕТРИКА», и после правки её можно пересчитать. Соревнование, метрика которого ни разу не считалась с картой, по-прежнему считает её в закреплённом образе посылки.
Подключите машину
- В панели откройте вкладку «Ресурсы» и в блоке «Машины» нажмите «Подключить машину».
- Задайте, сколько машин подключится по одному коду, сколько часов код действует (до 24), места на машину (пусто — по ядрам, см. места) и, если нужно, «Доверить машинам скрытый тест» — по умолчанию выключено. Строка «Принимать занятия» откроет машинам ещё и ядра классов (см. занятия на машинах); вместе с доверием её не ставят.
- Нажмите «Показать команды». Код покажется один раз — внутри трёх готовых команд. Выполните одну из них: первые две — на самой машине, от root; третью — у себя, из checkout репозитория.
# Docker уже стоит: Linux amd64, Docker Engine 28+; машине с GPU нужен ещё nvidia-container-toolkit
docker run -d --name colloq-runner --restart unless-stopped --network host --stop-timeout 90 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/lib/colloq-runner:/var/lib/colloq-runner \
-e COLLOQ_RUNNER_JOIN='https://colloq.example.edu#<код>' \
ghcr.io/colloq-edu/colloq-server:X.Y.Z runner
# Голая машина (Ubuntu): ставит Docker и colloq-host, а тот — NVIDIA Container Toolkit
command -v docker || curl -fsSL https://get.docker.com | sh
docker run --rm -v /usr/local/sbin:/host ghcr.io/colloq-edu/colloq-server:X.Y.Z install-host /host
colloq-host runner up https://colloq.example.edu <код>
# VM на Vast, из checkout репозитория (см. ниже)
make vast-runner COUNT=3 CODE=<код> URL=https://colloq.example.eduКод — 32 шестнадцатеричных знака. В первой команде он стоит в адресе после #: эта часть адреса не уходит в строку запроса. --network host ставит агента в сеть хоста: так он достаёт до ядер занятий, если машине их откроют (см. занятия на машинах). --stop-timeout 90 даёт агенту полторы минуты на остановку: за это время он возвращает серверу посылки, которые держит, и они не считаются потерянными. Код действует для заданного числа машин и не дольше суток. Сервер хранит только его отпечаток, поэтому показать код второй раз нельзя — выпустите новый. Отправляйте его тому, кто ставит машину, а не в общий чат: пока код действует, по нему подключится любая машина, и она увидит тетради участников.
Действующие коды перечислены в блоке под заголовком «Коды подключения»: на сколько машин, сколько уже подключилось, до какого времени код действует и кто его выпустил. «Отменить» закрывает код сразу: по нему больше не подключится ни одна машина, даже та, где команда уже вставлена, а подключившиеся продолжают работать. «Отключить все» отменяет и все действующие коды.
Под командами окно пишет «Ждём первую машину…», а потом называет подключившиеся. Окно можно закрыть: машины появятся в списке сами. Если адрес в командах — loopback этого сервера (localhost), окно предупреждает: машина в другом месте до него не дойдёт, задайте PUBLIC_URL. Подключённая машина сразу начинает скачивать образы сред и данные открытых соревнований, а посылки соревнования берёт, когда для него всё готово (см. готовность). С одного адреса сервер принимает не больше 10 подключений за 10 минут: машины за одним NAT подключайте партиями.
Без панели. Если до панели не добраться — например, на сервер ходят только по SSH через Teleport, — тот же код выпускает на машине сервера colloq-host runner-code. Он печатает код один раз, внутри тех же трёх команд:
colloq-host runner-code --machines 3 --hours 12
colloq-host runner-code --machines 2 --trusted --slots 4 --prefix lab--machines — сколько машин подключится по коду, от 1 до 100 (по умолчанию 1); --hours — сколько часов код действует, от 1 до 24 (24); --trusted — машины сразу получат скрытый тест; --classes — машины примут и занятия (вместе с --trusted не бывает); --slots — не больше стольких мест на каждой машине, от 1 до 64; --prefix — имена машин на сервере (lab-1, lab-2…; без него машину называет её имя хоста, и в панели это имя не меняется); --url — адрес, по которому машины придут к серверу (по умолчанию PUBLIC_URL).
Какой образ в командах. Тот, на котором работает сервер: опубликованный выпуск — как ghcr.io/colloq-edu/colloq-server:<версия>, образ из вашего реестра или зеркала — целиком, и тогда colloq-host runner up получает его третьим аргументом. Во втором случае команды make vast-runner нет: арендованные машины берут опубликованный образ, а не ваш.
Закрытый реестр. Первые две команды скачивают образ без входа в реестр, поэтому сначала войдите в него Docker машины, от root, с токеном только на чтение: docker login <реестр>. colloq-host runner up и runner update входят и сами, если им передать COLLOQ_REGISTRY_USER и COLLOQ_REGISTRY_TOKEN, — но только в реестр образа агента, а сами переменные в runner.env не сохраняются. Ядра агент скачивает по дайджесту из реестра, откуда их взял сервер (KERNEL_IMAGE_REPO), с тем входом root, который colloq-host передаёт контейнеру агента: если ядра лежат в другом закрытом реестре, войдите и в него. Агент, запущенный первой командой, этого входа не получает: закрытые ядра он скачает, только когда colloq-host пересоздаст его контейнер первым runner update (см. обновление).
Что нужно машине
- Linux на x86-64 и Docker Engine 28 или новее. Образ собирается только для amd64.
- Выделенная машина. Агент держит Docker socket, а это root на машине. Он отдаёт посылкам все карты, какие найдёт, и считает свободную память по всей машине, ничего не зная о комнатах сервера Colloq на том же Docker. Поэтому не ставьте его рядом с сервером, на котором идут занятия:
colloq-host runner upпредупреждает, если находит там контейнер сервера, а с--classesотказывает. Вуз одалживает соревнованию машину, которую может отдать целиком. - Диск: не меньше 40 ГБ без карт и 120 ГБ с картами — столько, сколько арендовано или куплено. Файловая система на диске показывает на несколько процентов меньше, и проверка оставляет на это 5 %. GPU-ядра занимают около 10–13 ГБ, сверху — кэш данных и пакетов и сами прогоны. Меньший диск машина показывает в панели как «на диске мало места»; когда место кончается, она сама удаляет то, что давно не нужно.
- Для видеокарт — драйвер NVIDIA 580 или новее и NVIDIA Container Toolkit. GPU-ядра собраны под CUDA 13.0, и на старом драйвере torch карты не видит.
colloq-host runner upставит toolkit сам, через apt, но не драйвер, и сразу говорит, годится ли драйвер. GPU-образ машина проверяет коротким запуском torch на той карте, где есть свободная доля, и не берёт GPU-посылок, пока проверка не прошла: в панели это «драйвер 535, нужен 580+ — GPU-образ не запустится». Если все доли заняты посылками, проверка ждёт и ошибкой не считается. Посылки на процессоре машина при этом берёт. - Только исходящие соединения. HTTPS к адресу сервера (
PUBLIC_URL;http://— только для сервера в той же сети), к реестру образов (ghcr.ioили ваш) и к индексу пакетов, откуда сервер брал «свои пакеты» участников; если индекс недоступен, машина берёт пакеты у сервера. Входящих портов не нужно. Исходящий прокси и внутренний CA (HTTPS_PROXY,NO_PROXY,NODE_EXTRA_CA_CERTS) передайте вызовуcolloq-host runner up: он запишет их в/etc/colloq/runner.env. Образы скачивает сам Docker машины, и прокси ему задают отдельно, как на сервере (см. исходящий прокси). - Каталог агента —
/var/lib/colloq-runner, на хосте и в контейнере по одному пути, с правами 0700; в нём удостоверение машины (runner.json, права 0600) и кэш данных и пакетов. Другой каталог задаётCOLLOQ_RUNNER_STATE— только отдельный, например/srv/colloq-runner: системные каталоги и каталог состояния сервера Colloq агент не примет.
Серверу машины добавляют свои требования к образам. Образ среды соревнования должен быть на сервере, когда тот закрепляет среду за соревнованием — при первом запуске его тетради, обычно при проверке бейслайна, — и когда он готовит «свои пакеты» участников; машины скачивают тот же образ из реестра. Дальше GPU-соревнованию, тетради которого считают только машины (тетради на сервере выключены или у сервера нет карты для посылок), свой образ на сервере для приёма посылок не нужен, когда сошлись три условия: его метрика считается в kaggle-base (тогда на сервере нужен он), ревизия среды есть в реестре, а за последний час на связи была машина с картами, на которую помещается его посылка, — память машины без 2 ГБ вмещает память посылки и ещё 256 МБ, а ядра машины без одного — ядра посылки. Соревнованию, метрика которого осталась на образах самих посылок (его первая метрика посчиталась без kaggle-base, см. метрику), свой образ на сервере нужен, как и раньше, а соревнованию на процессоре — всегда: в нём считается его метрика.
Места, карты и доверие
Места — сколько посылок машина считает одновременно. По умолчанию — авто: столько, сколько машина сама предлагает по своим ядрам и памяти. Себе и Docker она оставляет одно ядро и 2 ГБ памяти, а посылку берёт, только если пределы соревнования — ядра и память, и ещё 256 МБ сверху — помещаются в то, что осталось. Своё число, от 1 до 64, задают в окне подключения («Места» → «на машину»), пунктом «Места…» в меню строки или на странице машины; пусто — снова авто. Свободное место машины сервер занимает в том же честном порядке очереди, что и свои места.
Места панель считает в посылках того размера, какие есть: машина на 61 vCPU предлагает 60 мест, по ядру на посылку, а посылок по 2 ядра на ней по ядрам помещается 30 — и меньше, если их память, с 256 МБ сверху на каждую, не помещается 30 раз в то, что осталось от памяти машины; панель пишет меньшее число. Пульт соревнования и строка «Где считается» в редакторе считают места в посылках самого соревнования, список машин — в посылках, которые сейчас ждут или идут, а без них — в самой большой посылке открытого соревнования.
Карты. Машина отдаёт посылкам все свои карты — в отличие от сервера, где карты для посылок отмечает владелец. Сколько посылок делят одну карту, задаёт пункт «Посылок на карту…» в меню машины; пусто — как на сервере, в блоке «Видеокарты для посылок» («Посылок на одной карте»). Срез видеопамяти — память карты, делённая на это число, и держат его так же, как на сервере: ограничением PyTorch изнутри и сторожем снаружи (см. видеокарту для посылок). Карта ускоряет только то, что тетрадь сама переносит на CUDA, — модель и тензоры: тетрадь, написанная для процессора, и на машине с картой считает на процессоре. Машина без карт GPU-посылок не берёт.
Доверенная машина — единственная, что получает скрытый тест. Доверие дают при подключении («Доверить машинам скрытый тест») или потом — пунктом «Доверенная» в меню строки или на странице машины. Снятое доверие возвращает в очередь её прогоны со скрытым тестом, обрывает его скачивание и удаляет скрытый тест с машины. Пока доверенной машины нет, посылки со скрытым тестом считает сервер, если тетради на нём включены, а иначе они ждут: «ждёт доверенную машину: скрытый тест». Арендованной машине скрытый тест лучше не доверять: всё, что на ней лежит, видит хозяин хоста.
Список машин и пульт
Блок «Машины» сверху показывает сводку — сколько машин на связи, сколько у них мест и карт, — а ниже по строке на каждую машину: состояние, ресурсы, места («занято из»), готовность и меню.
| Состояние | Что оно значит |
|---|---|
| на связи | Отвечает, её код совпадает с сервером, берёт посылки. |
| молчит | Не выходила на связь дольше 3 минут («молчит 4 мин»). Если дорога к машинам работает, её прогоны возвращаются в очередь, и их посчитают другие; лимит не тратится (см. сбои). |
| устарела | Её код отличается от кода сервера: начатые посылки досчитывает, новых не берёт (см. обновление). |
| осушается | Досчитывает начатое и новых посылок не берёт. Её осушили пунктом «Осушить» в меню — обратно в работу её вернёт «Вернуть в работу»; или она останавливается либо уходит сама, и строка начинается словами «останавливается сама» или «уходит сама». Остановившаяся машина, запустившись снова, сама вернётся в работу; чтобы не вернулась — «Осушить». |
| на карантине | Сервер сам перестал давать ей посылки после череды потерь или отказов; над списком — баннер. |
| отключена | Её отключил владелец, и сервер больше не принимает её секрет. |
| ушла | Ушла сама: colloq-host runner leave. |
Готовность. Для каждого соревнования, которое открыто, откроется в ближайшие сутки или где ждут посылки, машина заранее скачивает образ среды, данные и, если она доверенная, скрытый тест, сверяя суммы. Посылки соревнования она берёт, только когда всё на месте; пакеты участников качает тоже заранее, а недостающие докачивает при подготовке посылки — время скачивания не входит во время тетради. В строке это выглядит как «образ ↓ 45 %», «данные ✓», «скрытый тест — только доверенным». Соревнование, среда которого собрана на этом сервере, помечено «считает только сервер»: её образа нет в реестре, и машины её не получат (см. среды из реестра). Подготовка, которая не удалась, называет причину: «драйвер 535, нужен 580+ — GPU-образ не запустится», «torch не видит CUDA — GPU-образ не запустится», «образ не скачался», «данные не скачались или не сошлись суммы», «на диске мало места».
Страница машины (пункт «Страница машины» в меню): что машина сообщила о себе — ядра, память, диск, карты, драйвер, Docker; её места, посылки на карту и доверие; готовность к каждому соревнованию; что она считает сейчас, с кнопкой «Остановить посылку» — остановка доходит до машины со следующим её откликом, до 15 с, а посылку, которая уже отдаёт или отправляет результат, с машины не снять; последние 50 событий и счётчики — сколько посылок она посчитала, сколько было потерь и отказов.
Пульт соревнования группирует исполнителей: «Сервер» и каждая машина со своими местами и картами; у идущей посылки видно, на какой машине и карте она считается, а метрики сервера шапка пульта считает отдельно — «МЕТРИКИ · 2 ИЗ 2». У ждущей посылки стоит причина, если та ждёт не просто своей очереди: «машины качают образ», «машины качают данные», «ждёт доверенную машину: скрытый тест», «ждёт карту: её нет ни здесь, ни на машинах», «не помещается ни на одну машину на связи», «некому считать: сервер считает только метрики, а машины работу не берут», «некому считать: для этого сервера посылка велика, а машины работу не берут», «вернулась: vast-1 замолчала». Посылка, тетрадь которой посчитана и ждёт метрики, — «оценивается».
«Работают сейчас» во вкладке «Ресурсы» показывает соревнование, тетради которого идут на машинах, отдельной строкой — с машинами под ней и памятью «здесь 0 МБ»: посылки на машинах памяти сервера не занимают, здесь остаются только их строки в очереди и метрика пришедших ответов. Кнопка «Остановить посылки на машинах» снимает их: на машине посылка останавливается со следующим её откликом, до 15 с, а участник попытку не тратит.
Редактор соревнования в строке «Где считается» говорит, кто возьмёт его посылки: сервер, сколько машин и мест, какие машины ещё готовятся и какие не подходят. Он же предупреждает, если среду собрали на этом сервере («только этот сервер») или если посылки со скрытым тестом некому считать.
Тетради на этом сервере и оценки
Над списком машин — два поля формы «Ресурсы»; сохраняет их кнопка формы «Сохранить изменения».
- «Тетради на этом сервере». По умолчанию — «считать и здесь»: тетради считают и сервер, и машины. С «только метрики» тетради считают только машины, сервер оценивает пришедшие ответы, а его ядра остаются занятию; шкала памяти той же вкладки отводит соревнованиям тогда только места оценок. Если при «только метрики» на связи нет ни одной машины, посылки ждут, и блок так и пишет, а сохранить такое положение форма даёт только после вопроса. Посылки среды, собранной на этом сервере, и посылки со скрытым тестом без доверенной машины при «только метрики» считать некому — редактор соревнования предупреждает об этом.
- «Оценок одновременно» — сколько метрик сервер считает одновременно: от 1 до 8, по умолчанию 2. Метрика — до 2 минут и 2 ГБ на ответ. Метрики ответов с машин и пересчёты идут по этим местам, а не по местам тетрадей. Память у тетрадей и метрик сервера общая: когда её хватает только на одно, первым идёт то, что ждёт дольше (присланное вовремя — раньше опоздавшего); ответ с машины ждёт с отправки своей тетради, пересчёт — с той минуты, когда его попросили. Приостановленная очередь держит новые тетради и пересчёты, но метрики ответов, которые уже вернулись с машин, идут и под паузой. На всплеске посылок, которые считают машины, узкое место — именно эти места: на сервере со свободными ядрами и памятью поднимите их число (см. проверку на Vast).
Обновление
Машина берёт новые посылки, только пока её код совпадает с кодом сервера — сборкой агента из того же выпуска. Выпуск, который не трогает код машин, оставляет их в работе как есть. Выпуск, который его меняет, переводит их в состояние «устарела»: начатые посылки они досчитывают, новых не берут, а в строке написано, например, «код 0.23.0, сервер 0.24.0 — новых посылок не берёт». Поэтому порядок такой: сначала сервер, потом машины.
# 1. Сервер, как обычно
colloq-host update ghcr.io/colloq-edu/colloq-server:X.Y.Z
# 2. Машины на Vast — все машины этого сервера, из checkout репозитория
make runners-update URL=https://colloq.example.edu
# или одна машина с colloq-host, по SSH, от root
colloq-host runner updatecolloq-host runner update узнаёт версию сервера по его /api/health, скачивает её образ, осушает машину — начатые посылки досчитываются, новых она не берёт — и запускает агента на новом образе; заодно обновляется и сам colloq-host. Если сама машина до /api/health не достаёт, версию называет агент, но только по синхронизации, сделанной уже после начала обновления: её команда ждёт до минуты (COLLOQ_RUNNER_FRESH_SEC), а без неё просит назвать версию. Осушение ждёт не дольше самого длинного прогона, какой допускает протокол (COLLOQ_RUNNER_DRAIN_SEC, по умолчанию 15 720 с). С --now оно не ждёт: начатые посылки возвращаются в очередь и считаются заново — без потерь и без траты попыток участников. Версию можно назвать и явно: colloq-host runner update 0.24.0. Образ без агента — выпуск до машин для посылок, например после отката сервера, — runner update и runner up отклоняют раньше, чем что-нибудь осушат или запустят: работающий агент остаётся на прежнем образе.
Машина, подключённая первой командой (docker run), colloq-host не имеет. Поставьте его из того же образа — docker run --rm -v /usr/local/sbin:/host <образ> install-host /host, — и первый же colloq-host runner update, leave, status или logs примет её контейнер: прочитает из него образ, адрес сервера, код и каталог и дальше ведёт машину так же, как поставленную через runner up.
make runners-update делает то же на всех машинах Vast сразу. На каждой машине обновление идёт в фоне и переживает обрыв SSH, а повторный вызов подхватывает уже идущее. Сначала команда до минуты ждёт, пока агент каждой машины заново синхронизируется с сервером: машину, код которой сервер в этой синхронизации признал своим, она не трогает (FORCE=1 — обновить всё равно), а машину без такого свежего ответа обновляет — дальше решает colloq-host на ней. NOW=1 — то же, что --now; VERSION= — одна версия для всех.
Перезапуск сервера посылки на машинах не обрывает: машины досчитывают их и отправляют результаты, когда сервер снова на связи, а первые 3 минуты после старта сервер ни одну машину не считает пропавшей. Обновляйтесь всё же между соревнованиями, а если не вышло — обновите машины сразу после сервера. Сами себя машины в этой версии не обновляют. Занятий на машине обновление не прерывает. Машину, поставленную через colloq-host до 0.25, для занятий нужно обновить дважды (make runners-update делает оба раза), а на машину, подключённую командой docker run, сначала поставить colloq-host — см. машину с занятиями.
Отключение и уход
colloq-host runner leaveна машине (машине, подключённой первой командой, сначала поставьте colloq-host, см. обновление): она досчитывает начатое, новых посылок не берёт, отзывает себя на сервере и удаляет контейнер агента. Кэш данных и пакетов остаётся в/var/lib/colloq-runner— удалите каталог, чтобы освободить место; образы ядер остаются в Docker машины (colloq-revision:<id>), их удаляетdocker image rmилиdocker image prune -a. Уход кончается ответом сервера: если сервер молчит 10 минут,colloq-hostперестаёт ждать и говорит об этом, а агент продолжает уходить — новых посылок не берёт и отзовёт себя, как только сервер ответит. Вернуть машину можно только новым кодом: со старым она не подключится, даже если on-start Vast повторит его при загрузке. Машина, на которой идут занятия, сначала спрашивает — см. машину с занятиями.make runners-down URL=https://colloq.example.edu— то же для машин Vast этого сервера, после чего их VM удаляются вместе с дисками; сначала команда спрашивает подтверждения. Машину, которая не ушла (например, её сервер молчал 10 минут), команда всё равно удаляет и говорит, что сервер её ещё числит: отключите её в панели. СNOW=1VM удаляются сразу, без ожидания: сервер потеряет машину через 3 минуты молчания и посчитает её посылки заново, а в списке она останется, пока её не отключат.- «Отключить» в меню строки (или «Отключить все» под списком): сервер сразу перестаёт принимать секрет машины. Её посылки возвращаются в очередь без потери лимита, поздние ответы не принимаются, а агент на следующем запросе удаляет скрытый тест и останавливается, чтобы больше не запускаться. Арендованную VM это не удаляет, и она тарифицируется, пока её не удалят. Сама машина обратно не подключится, но код, с которым она пришла, действует, пока не истечёт, не израсходуется или пока его не отменят в списке «Коды подключения»; «Отключить все» отменяет и все действующие коды.
Участникам уход машины ничего не стоит: посылки, которые она не досчитала, считаются заново на другом месте, а сам уход не тратит ни дневной нормы, ни попыток. Отключённая или ушедшая машина остаётся в списке ещё 30 дней, потом сервер удаляет её вместе с её событиями.
Если машина подводит
- Молчит. Машина, которую сервер не слышит 3 минуты, теряет свои прогоны: они возвращаются в очередь на своё место — раньше тех, кто пришёл после них, — и запускаются снова, на другой машине или на сервере. Участник читает: «Машина, на которой шла посылка, перестала её выполнять. Посылка вернулась на своё место в очереди и запустится снова; попытка не потрачена». Пропавшей машину сервер считает, только если знает, что дорога к машинам работает: другая машина отвечала в последнюю минуту или прошла его собственная проверка своего адреса. Если пропала сама дорога — упал туннель или прокси сервера, — ничего не теряется до 15 минут, а когда дорога возвращается, каждая машина снова получает полные 3 минуты.
- Отказывается. Машина, которая не может запустить посылку — нет образа, данных или пакетов, не хватает диска, памяти или доли карты, — возвращает её до старта контейнера. Посылка сразу уходит на другое место, а эта машина не получит её 10 минут. Участнику отказ ничего не стоит.
- Теряет снова и снова. Посылку, которую потеряли три разные машины или шесть раз — любые, сервер больше в очередь не ставит: она заканчивается сбоем сервера — «Это сбой сервера, а не вашей тетради; попытка не потрачена» — и не тратит ни нормы, ни потолка упавших; исполнить её заново можно из пульта. Так же заканчивается посылка, от которой отказались три разные машины.
- Зависает. Прогон, который тянется дольше своего предела — 10 минут на подготовку, время тетради, ещё 2 минуты и 10 минут на отправку результата, — сервер отзывает и считает потерянным, даже если машина продолжает отвечать.
- Карантин. Три потери или пять отказов на одной машине за 30 минут — и сервер сам её осушает: она досчитывает начатое и больше ничего не берёт, а над списком появляется баннер. Прогоны, потерянные разом, — например, все прогоны одного перезапуска агента, — это одна потеря, и считаются только потери после старта контейнера. Посмотрите события на странице машины и, устранив причину, нажмите «Вернуть в работу»: счёт карантина начнётся заново.
- Перезапуск агента. Если агент упал или машина перезагрузилась, Docker запускает его снова. При старте агент сначала убеждается, что прогоны его прошлой жизни уже не идут, и удаляет их контейнеры; результаты, которые он успел записать, всё равно отправляет, а остальные его прогоны сервер сразу считает потерянными и ставит в очередь заново. Остановленный агент (
docker stop,colloq-host runner update --now) сначала возвращает их серверу, без потерь.docker kill colloq-runnerдля Docker — остановка вручную: такой агент сам не перезапускается, его прогоны сервер вернёт в очередь через 3 минуты молчания, а контейнеры посылок остаются на машине, пока агента не запустят снова —docker start colloq-runnerили тем жеcolloq-host runner up, которым машину подключали; при старте он их уберёт. Контейнеры занятий агент при старте не убирает, а подбирает — с их переменными (см. занятия на машинах).
Занятия на машинах
Машина, которой владелец открыл занятия («Принимает занятия»), кроме посылок держит ещё и ядра классов — тех занятий, у которых в «Где считается» выбрано «На машинах». Это тот же агент, тот же образ и та же команда подключения; посылки такая машина берёт как раньше. Для каждого класса на ней работают:
- ядро тетрадей класса — общие тетради и терминал, один контейнер на класс; классу со средой на видеокарте достаётся целая карта машины;
- ядра личных тетрадей — все личные тетради класса в одном контейнере, до 60 ядер, без видеокарты;
- пул консилиума — исполнители попыток, если консилиум занятия считается на машинах: один контейнер на класс, без видеокарты (см. консилиум на машинах);
- копия папки занятия —
/var/lib/colloq-runner/rooms/<занятие>/, одна на контейнеры класса; у пула консилиума ещё/var/lib/colloq-runner/council/<занятие>/— снимки тетрадей и личные папки попыток. Каталог папок попыток (…/scratch) принадлежит root с правами 1733: таким его делает сам агент перед каждым запуском контейнера пула, чтобы попытки не находили папки друг друга (см. безопасность).
Пул консилиума загружает снимок тетради и считает попытки студентов одновременно, каждую в своём исполнителе. Его контейнер берёт на каждого исполнителя «Память на попытку», около 256 МБ на само ядро и распакованную часть снимка — строки, списки, небольшие объекты, которые каждый исполнитель держит у себя; один раз — числовые файлы снимка, общие для всех исполнителей; ещё 512 МБ запаса и по ядру процессора на исполнителя; он растёт и уменьшается вместе с пулом. Папку занятия он видит только для чтения, а файлы, которые пишут попытки, сервер забирает на их карточки. Без машин, у консилиума «В общем ядре» и пока пул поднимается, попытки считаются, как и раньше, по одной в ядре тетради — там, где оно работает.
Всё остальное остаётся на сервере: база, тетради с их выводом, сами файлы занятия (на машине — их копия), очередь ячеек. Jupyter ядер слушает только 127.0.0.1 машины, и сервер доходит до него через туннель — один WebSocket, который агент сам открывает к /api/runners/v1/tunnel по адресу сервера; входящих портов машине по-прежнему не нужно. Память и ядра процессора машины у посылок и занятий общие: занятия занимают их первыми, но идущую посылку ради занятия не останавливают. Переменные живут там, где ядро: обрыв связи их не стирает, а переезд ядра на другое место — стирает (см. переезд).
Как открыть машине занятия. Это может только владелец и только у машины без доверия: машине с занятиями скрытый тест не дают никогда. В окне подключения это строка «Принимать занятия — машина увидит код, данные, файлы и личные тетради студентов» (тогда «Доверить машинам скрытый тест» недоступно), у подключённой машины — «Принимает занятия» в меню строки или на странице машины, с подтверждением. Без панели такой код выпускает colloq-host runner-code --classes; вместе с --trusted он отказывает. «Не принимать занятия» сразу переносит занятия с машины — переменные пропадают, файлы остаются, — а посылки машина берёт как раньше. Подойдёт своя или университетская машина; арендованной давайте только учебные данные: всё на ней видит оператор хоста (см. изоляцию).
Что нужно машине сверх требований для посылок:
- Docker, запущенный от root и сам ведущий iptables (цепочка
DOCKER-USER): сеть каждого класса закрывают правила в ней. Rootless Docker,"iptables": falseвdaemon.jsonи nftables-бэкенд не подходят. - cgroup v2: по ней видно, сколько памяти брало ядро и отчего оно умерло.
- Ни одного сервера Colloq на этом Docker — ни его контейнера, ни комнат, ни их сети.
- Ещё 20 ГБ диска на папки классов сверх 40 или 120 ГБ машины для посылок.
- Агент в сети хоста (
--network host): так он достаёт до Jupyter ядер на127.0.0.1. Все команды подключения этой версии так его и запускают; о машинах, подключённых раньше, — ниже.
Для кода с занятиями вторая команда окна подключения — colloq-host runner up URL КОД --classes: она проверяет первые четыре пункта, прежде чем что-нибудь скачать или запустить, и называет всё, что не так. Подключённая машина сама сообщает серверу, что она может, и строка «Занятия:» в списке машин называет, чего не хватает: «нужен --network host — перезапустите машину командой из панели», «периметр: Docker без root или только с nftables — занятия нельзя», «на этой машине стоит сервер Colloq — занятия нельзя», «образ ↓ 45 % — возьмёт классы, когда скачает base». Прежде чем взять первый класс, машина проверяет песочницу изнутри — одноразовым контейнером с защитой класса: процесс не root, без capabilities и повышения прав, не видит Docker socket и файлов агента, не достаёт до метаданных облака, самой машины, её локальной сети, IPv6 и порта чужого класса, а папку класса читает и пишет. Пока проверка не прошла, классы на машину не едут: «проверка допуска не прошла: …», а пока на машине нет ни одного образа класса — «проверка допуска пройдёт, когда на машине будет образ класса».
Настройки владельца — блок «Занятия на машинах» во вкладке «Ресурсы»; действуют сразу:
- «Учителя отправляют занятия на машины» (включено): преподаватель сам выбирает «Где считается» у своего занятия; выключено — решает только владелец.
- «Новые занятия по умолчанию» — с каким набором создаётся занятие, по умолчанию «Всё здесь». Занятие со средой на видеокарте при любом наборе, кроме «Всё здесь», получает «Всё на машине с картой». Уже созданные занятия не меняются.
- «Переезд через» — 60 с, от 30 до 300: срок, через который переезжает занятие, пока ему не выбрали свой (см. переезд).
- «Файл с машины — до» — 200 МБ: файл крупнее, написанный ядром на машине, остаётся там (см. файлы).
- «Файлы занятий» — 4 МБ/с и «Раздача посылок во время занятий» — 2 МБ/с: пока на машинах идут занятия, файлы занятий в обе стороны идут не быстрее первого, а данные соревнований к машинам — не быстрее второго (без занятий — по умолчанию до 8 МиБ/с), на всех машинах вместе. Файлы, которых ждёт запуск ячейки, идут до 8 МБ/с. Ячейки, вывод и терминал эти пределы не трогают: машина лишь сглаживает свой вывод до 6 МиБ/с.
Где считается занятие
Где считать, выбирают у каждого занятия отдельно: в его настройках в панели — блок «Где считается» в разделе «Ресурсы» — и в окне «Новое занятие». Сверху — готовые наборы:
- «Всё здесь» — как было: ничего не уезжает. Так считаются занятия, созданные до 0.25, а новые — пока в «Новые занятия по умолчанию» у владельца стоит «Всё здесь».
- «Консилиум и личные тетради — на машинах» — тетради класса остаются здесь, и лекция идёт без задержки связи, а тридцать ядер личных тетрадей и попытки консилиума уезжают с сервера: попытки считает пул на машине, на снимке тетради класса (см. консилиум на машинах).
- «Всё на машинах» — здесь остаются только тетради и файлы. Занятию со средой на видеокарте вместо него предложено «Всё на машине с картой»: тетрадям класса — целая карта машины; личные тетради и пул консилиума карту не получают нигде, а снимок тетради с данными на видеокарте оставляет попытки в ядре тетради класса, на карте.
Занятия, которым в 0.25 выбрали набор «Личные тетради — на машинах» (так тогда назывался второй), «Всё на машинах» или «Всё на машине с картой» — при создании, в том числе через «Новые занятия по умолчанию», или позже в «Где считается» — и потом не вернули «Всё здесь», уже записаны с консилиумом «На машинах»: после обновления до 0.26 их попытки считает пул.
Ниже — те же части по отдельности: «Тетради класса» и «Личные тетради» — «Здесь» или «На машинах», «Консилиум» — «В общем ядре» (попытки по одной в ядре тетради класса, где бы оно ни было) или «На машинах». У консилиума на машинах ещё два числа: «Попыток одновременно» — «авто» (от 2 до 16 исполнителей, сколько поместится) или своё число, до 32, и «Память на попытку» — 1 ГБ по умолчанию: предел каждой попытки. И ещё две строки:
- «Если машин нет — здесь» (включено): если сейчас не подходит ни одна машина, ядро поднимается на этом сервере, если хватает памяти, и журнал ядра говорит: «Ни одна машина сейчас не может взять ядро — оно поднимается на этом сервере.» Выключено — запуск отказывает: «Сейчас ни одна машина не может взять это занятие, а на этом сервере ему считать нельзя.»
- «Если машина пропала» — «перенести через 60 с» (от 30 до 300) или «ждать машину» (см. переезд).
Под формой — машины, которые подходят этому занятию сейчас: «Подходят: vast-3 (свободно 41 ГБ), lab-2 (18 ГБ)», с пометкой «далеко: +317 мс на ячейку», если задержка связи с машиной (p95) больше 150 мс, — или почему не подходит ни одна: «мало памяти», «нет образа», «не на связи», «устарела», «на карантине», «недавно пропадала»… Отказы самого занятия: «Среда собрана на этом сервере — на машинах её нет» — на машины едут только среды colloq из реестра (base, base-gpu, kaggle-base, kaggle-gpu; см. среды из реестра); «Нужна видеокарта: на машине с картой»; «Машины не принимают занятия — Ресурсы → Машины».
Какую машину берёт сервер. Годится машина на связи, той же сборки, что сервер, открытая занятиям, не на карантине занятий, с поднятым туннелем, проверенной песочницей и образом класса (готовым или скачивающимся), у которой свободно памяти на ядро и ещё 256 МБ, хватает ядер, а классу с картой — целая свободная карта. Из таких сервер берёт ту, где уже есть другое ядро этого класса, потом ту, где образ уже готов, потом с меньшей задержкой связи и с большей свободной памятью. Пул консилиума прежде всего встаёт туда, где считаются тетради класса: тогда снимок не едет по сети. Образ среды машины скачивают заранее — как только занятие отправили на машины.
Кто выбирает и когда это действует. Преподаватели занятия, пока включено «Учителя отправляют занятия на машины», иначе только владелец; преподаватель тогда читает «Где считать занятия, сейчас решает владелец…». Новое значение действует с ближайшего запуска ядра: работающее ядро остаётся, где было, пока его не остановят или не перенесут. Ядра на машине держат память машины, а не сервера: в шкале памяти вкладки «Ресурсы» и в «Работают сейчас» они занимают здесь 0 МБ. На сервере в Kubernetes (Helm-чарт) этого блока нет: там у каждой комнаты свой Pod.
Сколько памяти и ядер можно дать. Память и ядра занятия ограничены местом каждой части, до которой они доходят: для части здесь — этим сервером, для части на машинах — самой большой машиной, открытой занятиям (её память без запаса для самой машины, ядра без одного), а если число доходит и сюда, и туда — тем, что теснее. Под числами форма пишет строку машин: «На машинах — память до 61,8 ГБ, ядер до 31: больше не даст самая большая из них, «vast-3».» Число больше границы сервер не сохранит и назовёт границу; перенести занятие туда, где его числа не дадут, он не даст, пока числа не уменьшат. Так же ограничены память и ядра личных тетрадей. Пула консилиума эти числа не касаются: его контейнер считается от «Память на попытку» и числа исполнителей.
Что видно в комнате и в панели
Студенты видят только то, что ядро на машине: «Ядро на машине» в строке тетради, без имени машины. Преподаватели видят имя и задержку связи: «Ядро · vast-3 · 52 мс». Ячейки, вывод, прерывание, перезапуск, input(), запуск файла, справка и терминал работают так же, как на сервере; справка по наведению и дополнение ждут ответа дольше — на дорогу до машины, до секунды сверху. Если ядро поднимается на машине дольше 8 секунд, журнал ядра пишет «Запускаем ядро на машине…», а преподаватели видят над тетрадью то же с именем машины и ходом загрузки образа: «Запускаем ядро на машине «vast-3»… образ ↓ 45 %».
Нет связи. Когда связь с машиной пропадает — туннель оборвался или 12 секунд от машины ничего не приходит, — строка тетради желтеет: «Нет связи с машиной · 0:12». Когда связи нет 10 секунд, журнал ядра пишет: «Нет связи с машиной · 0:12. Ядро и переменные на ней целы — ждём связь. Новые запуски подождут.» Ячейки в очереди ждут с пометкой «ждёт машину»; перезапуск и остановка ячейки в это время отказывают тому, кто нажал: «Нет связи с машиной — перезапуск сейчас не дойдёт. Нажмите, когда связь вернётся.», «Нет связи с машиной — остановить ячейку сейчас нельзя. Предел времени сработает, когда связь вернётся.» Когда связь вернулась, запуски продолжаются в том же ядре, а журнал пишет «Связь с машиной вернулась через 37 с.» Если обрыв съел ответ ячейки, это сказано в ней: «Связь с машиной прервалась — результат ячейки потерян. Ядро и переменные целы; запустите ячейку снова.» Перезапуск, ответ на который потерялся в обрыве, сервер выясняет, когда связь вернётся: «Перезапуск прошёл: переменные сброшены.» или «Перезапуск не дошёл — переменные на месте.»
Плашка для преподавателя. Через 10 секунд без связи над тетрадью у преподавателей появляется, что будет дальше, — например «Машина «vast-3» не отвечает с 14:32:05. Ждём до 14:33:05, потом поднимем ядро заново (переменные пропадут).», — и кнопки. «Перенести сейчас» переносит ядро сразу, не дожидаясь срока: переменные пропадают, а ядро поднимется при следующем запуске. «Ждать машину» ставит занятию «ждать машину»: ядро не переедет само, пока не нажмут «Перенести сейчас» («Ждём машину: ядро не переедет само, пока вы не нажмёте «Перенести сейчас».»), а вернуть срок можно в «Где считается»; эту кнопку видит тот, кто может менять «Где считается». Пока агент машины обновляется, журнал пишет «Агент на машине обновляется. Ядро и переменные на ней целы — новые запуски подождут.»
В панели. В списке «Машины» у машины две метки, «посылки» и «занятия», и строка вроде «Занятия: 3 класса · 22 из 60 ГБ · карта 0 занята классом · связь 48 мс (p95 80)». Страница машины перечисляет её занятия — ядра, память, процессор, связь и файлы каждого — и события: переезды с причиной, замены туннеля с адресами, карантин. «Перенести занятия» в меню машины сразу переносит все её занятия — переменные пропадают, файлы остаются, — а машина и дальше принимает занятия. «Работают сейчас» показывает занятие с ядрами на машинах с подстроками «класс», «личные» и «консилиум» (у пула — ещё число исполнителей, «3 исп.»): где они считаются, сколько памяти и процессора берут и как идут файлы — «файлы: синхронно», «файлы: ↑ 3, 12 МБ», «файлы: 2 только на машине»; здесь такие ядра занимают 0 МБ. Остановка ядер на машине без связи выполнится, когда она выйдет на связь.
Файлы занятия на машине
Файлы занятия хранит сервер, на машине — их копия, и она догоняет сервер в обе стороны. Что изменили здесь — загрузили, сохранили в редакторе, записало ядро на сервере, — уезжает на машину через секунду, а перед каждым запуском ячейки, файла или попытки на машине сервер сверяет, что копия догнала. Если ничего не менялось, запуск ничего не ждёт. Если едет крупный файл, ячейка показывает «Файлы едут на машину: 12 из 150 МБ…», а когда 30 секунд ничего не движется или прошло 2 минуты, не запускается: «Файлы не дошли до машины: 30 с без движения — ячейка не запущена.»
Что ядро написало на машине, едет сюда — после каждого запуска, а пока ядро считает, ещё и каждые 5 секунд (без дела — каждые 30). Файл берётся, когда 2 секунды не менялся, а новая папка — когда в ней всё улеглось, так что распаковка и pip install не едут наполовину. Новый файл появляется в панели файлов, когда он уже здесь; у файла, новая версия которого ещё в пути, стоит «Новая версия едет с машины», и скачивание дождётся её.
- Оба изменили один файл. Остаётся версия сервера, а версия с машины ложится рядом копией: «data.csv изменили одновременно здесь и на машине. Оставлена версия сервера, версия с машины — «data (с машины 14-32).csv».»
- Здесь удалили, там переписали. Удаление побеждает: файл не возвращается, а журнал ядра пишет «old.csv удалён на сервере; изменения, сделанные в нём на машине, отброшены.»
- Переименовали на машине. Здесь он переименовывается тоже, без пересылки.
- Тетради. Файлы тетрадей занятия принадлежат серверу: ядро на машине не может их переписать или удалить — сервер возвращает свою версию.
Что остаётся на машине. Файл крупнее «Файл с машины — до» (200 МБ) или не помещающийся в предел занятия («Все файлы занятия до», MAX_SESSION_MB, 1 ГБ) сюда не едет: в панели файлов он помечен «на машине», и преподаватель может его «Привезти» — так приезжает и файл крупнее 200 МБ, лишь бы помещался в предел занятия, а прокси сервера пропускал такое тело (см. обратный прокси). Файл, тело которого прокси не пропустил (ответил 413), тоже остаётся на машине, пока не изменится: журнал ядра говорит «model.pt (150 МБ) остаётся на машине: прокси перед этим сервером не принял файл такого размера. …», а «Привезти» отказывает, пока предел прокси не поднимут. Файлы сверх 5000 в папке тоже остаются на машине. Пакеты, поставленные в папку занятия (pip install -t .), не едут совсем и видны одной строкой «пакеты (pip -t) — на машине, 2,1 ГБ». Ссылки, скрытые файлы, __pycache__ и временные файлы не синхронизируются вовсе. Всё, что «на машине», пропадёт вместе с ней, о чём журнал ядра говорит один раз: «model.pt (2,1 ГБ) больше предела 200 МБ и остаётся на машине. Если машина пропадёт, файл пропадёт тоже.» Удаляют такой файл, как любой: «Удалить» в панели файлов (преподаватель) удаляет его на машине, и строка уходит, как только машина его удалила.
Перед концом занятия (до 2 минут), сборкой его страницы (до 15 секунд), переносом ядра с машины, которая отвечает, и снятием с неё «Принимает занятия» (до 10 секунд) сервер дожидается файлов с машины; машина, которая уходит (runner leave), сама сначала отправляет файлы своих занятий (до 10 секунд). Когда ядра класса на машине останавливают — конец занятия, остановка владельцем, простой, — изменения, которые не успели доехать, названы в журнале ядра: «Ядра на машине остановлены; изменения в файлах, которые не успели доехать до сервера, отброшены: out.csv, model.pt.» После переезда файлы такие, какими дошли в последний раз («файлы — по 14:31:58» в записи о переезде), а машина, вернувшаяся после переезда, ничего здесь не перезаписывает: её поздние изменения отброшены — «Машина вернулась после переезда; её изменения в файлах после 14:32 не приняты: out.csv, model.pt.», — а копия папки на ней удаляется.
Если машина с занятием пропала
Обрыв связи — ещё не потеря. Агент сам поднимает туннель снова, и то, что было в пути, досылается, если обрыв короче двух минут; ядро и его переменные на машине всё это время целы. Ядро переезжает, только когда сервер уверен, что машины нет:
- Машина молчит — ни туннеля, ни её запросов — дольше срока занятия («перенести через 60 с»), а дорога к машинам при этом работает: другая машина на связи или прошла проверка, которую сервер, пока идут занятия на машинах, каждые 5 секунд делает своему адресу (см. сервер). Если пропала дорога самого сервера, не переезжает ничего: до занятия тогда всё равно никто не дотянется.
- Туннель не поднимается, хотя машина отвечает на запросы, — через 2 минуты: считать занятие она всё равно не может.
- Сразу — машину отключили, сняли с неё «Принимает занятия», она ушла (
runner leave), её сеть вышла из-под контроля или преподаватель нажал «Перенести сейчас».
В первые 3 минуты после старта сервера и в первые 2 минуты после того, как агент ушёл на обновление (colloq-host runner update), не переезжает ничего. Занятие с «ждать машину» не переезжает по времени вовсе — только по «Перенести сейчас».
При переезде переменные пропадают, очередь сбрасывается («Очередь сброшена: ячейки рассчитывали на переменные, которых больше нет.»), а у шедшей ячейки написано «Эта ячейка выполнялась на машине, когда та пропала. Неизвестно, успела ли она закончиться; на новом ядре её нет.» Журнал ядра комнаты пишет «Машина не отвечает с 14:32:05. Ядро поднимется заново: переменные пропали, файлы — по 14:31:58. Запустите ячейки снова.», а преподаватели читают то же с именем машины. Ядро поднимается при следующем запуске: на другой подходящей машине — на ту, с которой ушло, оно полчаса не вернётся, — иначе на этом сервере, если включено «Если машин нет — здесь» и хватает памяти; журнал скажет, где («Ядро поднято на другой машине.»). Если пропала машина личных тетрадей, тетради класса не трогаются: переменные теряют только личные тетради, и их ядра поднимутся при следующем запуске. Если пропала машина пула консилиума, а тетради класса считаются не на ней — здесь, как в наборе «Консилиум и личные тетради — на машинах», или на другой машине, — ядро тетради не трогается: попытка, которая считалась там, один раз пересчитывается в ядре тетради, ждавшие уходят туда же, а пул поднимается там, куда встанет, и загружает снимок заново. Но пул прежде всего встаёт к тетрадям класса и при «Всё на машинах» и «Всё на машине с картой» обычно делит с ними машину: тогда её пропажа — переезд и для тетрадей. Их переменные пропадают, как сказано выше, попытки пересчитываются уже в новом ядре тетради, без переменных преподавателя, а пул начинает со снимка нового ядра — подготовительные ячейки нужно выполнить снова (см. консилиум на машинах). Ядро, которое на машине упало само (например, кончилась память), поднимается там же, с теми же словами, что на сервере.
Что переживает.
- Перезапуск агента — упавшего или убитого
kill -9внутри контейнера: Docker поднимает агента снова, а тот подбирает работающие контейнеры классов. Переменные и терминал целы. colloq-host runner update: старый агент, уходя, говорит серверу, что вернётся, и 2 минуты сервер его занятия не переносит, а новый агент подбирает их контейнеры. Переменные целы.- Перезапуск сервера: ядра на машинах продолжают работать, сервер снова подключается к ним, и переменные целы; очередь ячеек, как и у ядер на сервере, теряется. Файлы, которые ядро написало, пока сервера не было, приезжают после его старта.
docker kill colloq-runnerдля Docker — остановка вручную: агент сам не поднимется, и через срок занятия его занятия переедут.
Карантин занятий. Машина, дважды пропавшая за 30 минут (молчание или туннель; переносы вручную не считаются), новых занятий не получает: над списком машин — «Машина «vast-3» дважды пропадала за 30 минут — занятия на неё больше не отправляются.» с кнопкой «Вернуть», а в меню машины — «Вернуть занятиям». Посылки она берёт как обычно: это не карантин посылок.
Перехват связи. У машины один туннель: новый закрывает прежний, и замены записываются в события машины с обоими адресами. Если за 10 минут связь перехватывали с разных адресов больше трёх раз, над списком появляется «Связь машины «vast-3» за 10 минут 4 раза перехватывали с разных адресов: 5.6.7.8, 9.10.11.12. Проверьте, не утёк ли её ключ.» — так выглядит украденный секрет машины. Отключите её и подключите новым кодом.
Сеть машины проверяется каждую минуту, пока на ней занятия: пропавшее правило ставится снова, а после трёх неудач подряд машина сама останавливает контейнеры классов, они переезжают, и над списком машин появляется баннер.
Машина с занятиями: обновление, уход, состояние
colloq-host runner statusпечатает, как раньше, строку JSON агента, а рядом, в stderr, — туннель и занятия:tunnel: up · RTT 48 msиclasses: room 1 · own 1 · council 0, сколько контейнеров классов держит машина. Если агент работает не в сети хоста, команда предупреждает, что занятий он не возьмёт.colloq-host runner updateзанятий не прерывает: агент осушается, уходит с пометкой «перезапуск», и новый агент подбирает контейнеры классов. Обновление, которое меняет протокол туннеля, пока на машине идут занятия, отказывает, ничего не тронув, — если только новый образ не тот выпуск, на котором уже работает сервер. Поэтому порядок прежний: сначала сервер, потом машины.make runners-updateобновляет так же.- Осушённая машина — «Осушить» в меню или начало
runner updateилиrunner leave— досчитывает посылки и не берёт ни новых посылок, ни новых занятий, а уже идущие на ней занятия продолжаются. Осушение самого агента, которое запускаетrunner update(docker exec colloq-runner colloq-vast runner drain), и его уход ждут только посылки: занятия они не считают. colloq-host runner leaveна машине с занятиями сначала отказывает и ничего не меняет (код выхода 3): уход перенесёт занятия, и их переменные пропадут. С--forceмашина уходит как обычно: досчитывает посылки, отправляет серверу то, что ядра её занятий успели написать (до 10 секунд), а когда отзывает себя, её занятия переезжают, и агент удаляет их контейнеры и копии папок.make runners-downотвечает за вас--force: он уже спросил, удалять ли машины.
Переход с 0.24. Машина, подключённая до 0.25, работает в сети Docker, а не хоста. До первого обновления она для сервера 0.25 устарела, как после любого выпуска: не берёт ни новых посылок, ни занятий. Это обновление выполняет ещё прежний colloq-host, а новый ставится только в его конце, поэтому агент после него всё ещё не в сети хоста: посылки берёт, а занятий не возьмёт, строка машины говорит «нужен --network host — перезапустите машину командой из панели», и runner status об этом предупреждает. Переносит его второй colloq-host runner update — уже новым colloq-host. make runners-update делает второе обновление сам, а VM на Vast исправляется и при перезагрузке: её on-start ставит свежий colloq-host и повторяет runner up. На машину, подключённую командой docker run, поставьте colloq-host из образа новой версии (см. обновление): его первый runner update сразу перенесёт агента в сеть хоста.
Машины на Vast
Из checkout репозитория, с VAST_TOKEN и SSH-ключом в .env, как для make vast-vm:
make vast-runner COUNT=3 CODE=<код> URL=https://colloq.example.edu
make vast-runner COUNT=2 GPU="RTX 4090" GPUS=2 DISK=150 CODE=<код> URL=https://colloq.example.eduКоманда арендует COUNT VM, от 1 до 16, тем же путём, что make vast-vm: с теми же фильтрами предложений (GPU=, GPUS=, GPU_RAM=, CPU=, RAM=, DISK=, MAX_PRICE=) и тем же порогом Max CUDA 13.0. Сначала она пишет, что арендует и сколько самое большее платит за час каждой машины (MAX_PRICE), и спрашивает подтверждения (FORCE=1 — без вопроса); найденное предложение и его цену каждая машина называет уже после этого. Машины арендуются по очереди — каждая берёт самое дешёвое подходящее предложение — и загружаются одновременно; каждая ждёт, пока её агент подключится. Код должен быть рассчитан хотя бы на COUNT машин; URL= можно один раз записать в .env как VAST_RUNNER_URL.
Диск по умолчанию — 120 ГБ, нижняя граница для машины с картами: VM без карт на Vast не бывает, а машине с картами нужны GPU-ядра. Машина для посылок на процессоре — это VM подешевле с нужным числом ядер (CPU=), и её карта тоже возьмёт GPU-посылки.
В on-start арендованной VM — только адрес сервера и код: ни токена relay, ни других ключей. On-start видит оператор хоста, и Vast хранит его в консоли, а код ничего не стоит не позже чем через сутки или когда подключились все машины, на которые его выпустили (команда из окна подключения ставит COUNT равным их числу). Версию образа машина узнаёт у сервера, поэтому после обновления сервера новый on-start не нужен — нужен make runners-update.
Машины помечаются на Vast меткой colloq-runner (с NAME= — colloq-runner-<NAME>), и по ней их находят make runners-update и make runners-down. Каждую машину они сначала спрашивают по SSH, какому серверу она служит; с URL= берутся только машины этого сервера, без него — все машины аккаунта с этой меткой.
Оплата — ваша. Машины тарифицируются на вашем аккаунте Vast, пока их не удалят. В том числе те, что арендовались, но не подключились (сервер не ответил, у кода кончились подключения): их нет ни в одном списке машин. Команда в конце называет каждую такую машину и печатает команду её удаления, а с DESTROY_UNJOINED=1 удаляет их сама. Машина, отключённая в панели, тоже тарифицируется, пока её не удалят — make runners-down или в консоли Vast.
Среды из реестра и GPU-ядра
Образ среды машина скачивает не у сервера, а из реестра — по дайджесту, который сервер записал, когда сам получил этот образ, — и перед запуском сверяет его слои с образом сервера. Поэтому машины считают только те среды, образ которых сервер скачал из реестра. base и kaggle-base публикуются с каждым выпуском; GPU-ядра base-gpu и kaggle-gpu — отдельной командой make kernel-publish на машине с картой, при выпуске (RELEASING.md). Сервер берёт их из реестра, если они перечислены в KERNEL_PRELOAD, например KERNEL_PRELOAD=base,kaggle-base,base-gpu,kaggle-gpu.
Среду, собранную или изменённую на этом сервере, в этой версии считает только сервер: её образа нет в реестре. В списке машин такое соревнование помечено «считает только сервер», в редакторе — «только этот сервер». С KERNEL_IMAGE_REPO=none сервер собирает все среды сам, и машинам считать нечего. Так же остаётся на сервере GPU-ядро, которое он собрал сам, пока ядро его версии ещё не было опубликовано; как перейти на опубликованное — в том же разделе RELEASING.md.
Проверено на Vast
14 октября 2026 года, в два захода, на двух сборках этой версии до выпуска. Сервер — VM на Vast (61 vCPU, 86 ГБ, RTX A2000 на 6 ГБ) за relay, с «Тетради на этом сервере» в положении «только метрики»: каждый запрос машин шёл через relay. В первом заходе машина была одна — на той же VM, что и сервер (только для опыта: в работе машина должна быть отдельной); во втором к ней добавилась отдельная VM с 1 × RTX 4090 на 24 ГБ, 35 vCPU и драйвером 580.95.05.
| Что мерили | Сколько |
|---|---|
| Что машина добавляет к посылке при тёплом кэше | ≈ 2,6 с: тетрадь на 15 с получила вердикт за 28,4 с против 25,8 с на сервере |
| Файл данных на 150 МБ через relay | ≈ 16 с со снятым ограничением скорости, около 9,5 МБ/с одним потоком; по умолчанию сервер раздаёт данные всем машинам вместе не быстрее 8 МиБ/с |
| Готовность машины к соревнованию | kaggle-base из GHCR — около 2,5 мин; kaggle-gpu на 12,7 ГБ из реестра лаборатории вместе с проверкой карты — около 4 мин |
| 52 посылки разом | все получили оценку за 60 с на двух машинах; вердикт — медиана 44 с, последний 59 с |
| Эталон GPU-соревнования по распознаванию букв, по 2 посылки на A2000 | 6 из 6 оценены, около 90 с каждая — на процессоре: эталон ничего не переносит на CUDA; проверка бейслайна на доле карты — 18 с |
Узкое место всплеска — метрики на сервере. В первом заходе, с одной машиной и «Оценок одновременно» на 2, хвост всплеска из 30 посылок ждал метрик (по 4,6 с на ответ), и последний вердикт пришёл через 96 с. Всплеск из 52 посылок во втором заходе закончился за минуту при 8, но у него были ещё две машины и новая сборка, поэтому, сколько дали одни эти 8, два всплеска не показывают. Если у сервера есть свободные ядра и память, поднимите «Оценок одновременно» перед соревнованием с машинами. И помните про CUDA: эта эталонная тетрадь ничего не переносит на карту, поэтому и на машине с картой считает на процессоре — во втором заходе по 80 с на посылку.
| Что сделали | Чем кончилось |
|---|---|
Убили процесс агента посреди 10 прогонов (kill -9 внутри контейнера) | Docker запустил агента снова; тот убрал 10 контейнеров посылок прошлой жизни и сообщил, что их прогонов больше не держит. Сервер сразу вернул их в очередь, машина на карантин не ушла, все 10 оценены по одному разу. |
| Перезапустили сервер посреди прогонов | Машина продолжала прогоны и, пока сервер поднимался, повторяла запросы (relay отвечал 404); все 10 результатов приняты после старта, потерь нет. |
| Оборвали туннель relay на 4 минуты посреди прогонов | Ни один прогон не потерян: дорога к машинам пропала, и сервер ждал. Прогоны досчитались, отправка повторялась, все 6 результатов приняты, когда туннель вернулся. |
| Отрезали одну машину от relay, пока другая была на связи, но осушена | Примерно через 190 с её 8 прогонов потеряны. Они ждали, пока вторую машину не вернули в работу, и посчитались на ней; 8 поздних результатов вернувшейся машины сервер отбросил. Каждая посылка оценена один раз, карантина не было. |
| Отключили машину посреди прогонов, потом подключили новым кодом | Агент получил 401, бросил прогоны, удалил их контейнеры и остановился, выключив свой перезапуск; 4 посылки вернулись в очередь без потерь. По новому коду машина пришла как новая, и 4 посылки оценены. |
| Сняли доверие с машины, которая скачала скрытый тест | Скрытый тест удалён с машины, посылка с ним ждала «ждёт доверенную машину: скрытый тест»; после нового доверия тест скачан заново, посылка оценена. |
| Остановили посылку, которая шла на машине | Контейнер посылки исчез через 1,3 с, посылка отменена. |
| Обновили сервер на новый код машин | Старая машина стала «устарела» и посылок не брала; colloq-host runner update осушил, заменил и снова подключил её за 6,7 с, и ждавшие посылки оценены. |
Найденное репетицией исправлено до выпуска. Одна остановка агента посреди 10 прогонов сразу уводила машину на карантин; сборка второго захода считает её одной потерей, и это видно по агенту, убитому в том заходе (выше). Три исправления сделаны уже после обеих сборок, и на Vast их не проверяли: панель писала места по ядрам машины, а не по посылкам соревнования; GPU-соревнование требовало на сервере свой образ на 13 ГБ, даже когда тетради считали только машины; сервер, обновлённый прежним colloq-host, называл в командах опубликованный образ, даже работая на своём. Ещё репетиция показала, что docker kill — остановка вручную, а не сбой (см. сбои).
Чего машины пока не делают
- Не готовят «свои пакеты» участников: наборы готовит сервер (на Docker-пути ему нужен Docker Engine 28 или новее), машины только скачивают готовые.
- Не дают пулу консилиума видеокарту, и на самом сервере пула нет: без машин попытки считаются по одной в ядре тетради.
- Не переносят переменные: ядро, переехавшее с машины, поднимается заново.
- Не раскладывают личные тетради класса по машинам: все они — в одном контейнере на одной машине, а тетради класса могут считаться на другой или здесь.
- Не обновляются сами: после обновления сервера нужен
make runners-updateилиcolloq-host runner update. - Не подписывают запросы. Машина предъявляет свой секрет в заголовке, а TLS заканчивается на обратном прокси или relay перед сервером: кто управляет ими, может прочитать секреты машин (см. изоляцию).
- Не продолжают прогон после перезапуска агента: такой прогон считается заново.
- Одна машина служит одному серверу. Арендовать машины из панели нельзя — только
make vast-runnerили руками. - Сервер в Kubernetes (Helm-чарт) машин в этой версии не подключает: блока «Машины» в его панели нет, а у каждой комнаты там свой Pod.