Я сдаюсь. Каким-то чудом связка gitlab-runner и Podman отработала один раз, но как для этого должны были сойтись звезды и ощущалось ли влияние ретроградного Меркурия, выяснить не удалось. И все же отрицательный результат - тоже результат, поэтому статью опубликовал, вдруг она натолкнет вас на какие-нибудь идеи. Все же хостить эти самые страницы возможно, а марафонца запускать в другой виртуальной машине или еще что-нибудь придумать.

Статья продолжает серию экспериментов с GitLab во FreeBSD. Для этого я пользуюсь виртуальной машиной VirtualBox 7.2. Ранее также был установлен Podman, реестр контейнеров и Caddy в качестве обслуживающего все это дело веб-сервера. Версия GitLab – 18.3.2 (да, тут уже вышла 18.4.0, но я не верю, что это что-то изменит). GitLab Runner отстает: 18.3.0 или 18.3.1.

GitLab Pages

Начнем с сервиса страниц как такового, сам по себе он во FreeBSD хотя бы работает. Включаем и настраиваем их в /usr/local/www/gitlab/config/gitlab.yml на хост pages.local (для примера):

  ## GitLab Pages
  pages:
    enabled: true
    access_control: false
    host: "pages.local"
    artifacts_server: false
    secret_file: /usr/local/www/gitlab/.gitlab_pages_secret
    namespace_in_path: true

Отключаем контроль доступа (он почему-то по умолчанию включен, хотя настраивается вроде как довольно сложным образом) и «сервер артефактов», но включаем функцию пространства имен в путях.

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

su -l git -c "openssl rand -base64 32 > /usr/local/www/gitlab/.gitlab_pages_secret"
chmod 640 /usr/local/www/gitlab/.gitlab_pages_secret
chgrp gitlab-pages /usr/local/www/gitlab/.gitlab_pages_secret

Заодно установили нужные права доступа. «Симметрично» настраиваем /usr/local/share/gitlab-pages/gitlab-pages.conf:

listen-proxy=localhost:8090
pages-root=/usr/local/www/gitlab/shared/pages
api-secret-key=/usr/local/www/gitlab/.gitlab_pages_secret
pages-domain=pages.local
internal-gitlab-server=http://192.168.56.143
namespace-in-path=true

Вместо listen-http я написал listen-proxy и явно localhost, чтобы не было доступа извне непосредственно на порту. Также оказалось обязательным повторить настройку пространства имен в путях (подсмотрел на рабочем сервере). Поскольку в качестве веб-сервера я использую Caddy, добавляем обслуживание pages.local в /usr/local/etc/caddy/Caddyfile:

http://pages.local {
    reverse_proxy localhost:8090
}

Включаем и (пере)запускаем сервисы:

service gitlab_pages enable
service gitlab_pages start
service gitlab restart
service caddy restart

Сопоставляем хост pages.local с IP виртуальной машины, например в Windows 10 это файл C:\Windows\System32\drivers\etc\hosts (редактировать с правами администратора):

192.168.56.143 pages.local

Думаю, целесообразно внести запись и в /etc/hosts самой FreeBSD, если вдруг домен понадобится бегуну или еще кому. Вероятно лучше чтобы это тоже был IP виртуалки для доступа из контейнеров.

GitLab Runner

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

pkg install gitlab-runner

Справедливости ради, версия в пакете может быть не самой актуальной. После установки выводится краткая инструкция по использованию:

Message from gitlab-runner-18.3.0:

--
To enable gitlab-runner:

  sysrc gitlab_runner_enable=yes

To start gitlab-runner:

  service gitlab_runner start

To register with GitLab:

  service gitlab_runner register

To list status of GitLab:

  service gitlab_runner list

В принципе это правда, но с настройками по умолчанию runner будет запущен в пользовательском режиме, что нам не подходит. Забегая вперед, понадобится доступ к сокету Podman’а, который во FreeBSD есть только у суперпользователя. Поэтому пока войдем в GitLab под root, административную панель, CI/CD – Runners и создадим гонца из Пизы. Со времен OL8 и 17-го GitLab по сути ничего не поменялось – предлагаю так же поставить метку pages и некое описание:

instance_runner.png

Разумеется, про FreeBSD после создания не будет ни слова, так что додумаем сами. Вопреки предложенному ранее, выполняем:

gitlab-runner register

Поскольку дело происходит под root (FreeBSD-шным), то команда выполнится в системном режиме. Будет задано несколько вопросов; токен со страницы создания гонца потребуется для ответа на второй.

Enter the GitLab instance URL (for example, https://gitlab.com/):
http://192.168.56.143/
Enter the registration token:
glrt-xxx...
Verifying runner... is valid                        correlation_id=XXXXXXXXXXXXXXXXXXXXXXXXXX runner=xxxxxxxxx
Enter a name for the runner. This is stored only in the local config.toml file:
[vbox]:
Enter an executor: docker+machine, docker-autoscaler, shell, parallels, docker, docker-windows, kubernetes, instance, custom, ssh, virtualbox:
docker
Enter the default Docker image (for example, ruby:3.3):
docker.io/freebsd/freebsd-static:14.3
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

Обратите внимание, что исполнитель все равно docker, ну и образ по умолчанию я указал «какой-то» бсдшный, хотя вряд ли он потребуется.

Настраиваем и запускаем сервис:

sysrc gitlab_runner_enable=yes
sysrc gitlab_runner_user=root
sysrc gitlab_runner_dir=/usr/local/etc/gitlab-runner
service gitlab_runner start

В панели GitLab проверяем, есть ли контакт:

runners.png

Время этого самого контакта должно быть «just now» или приблизительное по смыслу. Однако настройка на этом не заканчивается. Сокета Докера у нас очевидно нет (к которому попытался бы обратится gitlab-runner), поэтому включаем и запускаем сервер API:

service podman_service enable
service podman_service start

Благодаря этому появляется «правильный» сокет Подмана, который прописываем в /usr/local/etc/gitlab-runner/config.toml:

  [runners.docker]
    host = "unix:///var/run/podman/podman.sock"
    # ...

Еще один момент – нужно заранее скачать линуксовый вспомогательный образ (тег должен соответствовать версии бегуна), иначе Podman будет безуспешно искать вариант для FreeBSD:

podman pull --os=linux registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:x86_64-v18.3.0

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

Тестовый проект

Как обычно, нам не до жиру, а хотя бы базовый HTML развернуть. Создадим новый проект на основании шаблона Pages/Plain HTML. После создания в Settings – General, разделе Visibility, project features, permissions отключим ненужное, то есть по сути все, кроме репозитория как такового и CI/CD.

Настраиваем Deploy – Pages. Как я писал в начале, развертывание единожды прошло успешно, и в тот момент стоял образ Alpine Linux. Поэтому в примере указал его, хотя на самом деле вряд ли это на что-то влияет и можно взять тот же busybox. Директория public остается, установочные шаги пропускаем, а при сборке выведем сообщение об URL публикации. Окончательно довести настройки до ума нужно самостоятельно путем правки черновика, требуется явно указать платформу для эмуляции и метку нашего зожника. Получится примерно следующее:

# The Docker image that will be used to build your app
image:
  name: docker.io/library/alpine
  docker:
    platform: "linux/amd64"
create-pages:
  pages:
    # The folder that contains the files to be exposed at the Page URL
    publish: public
  rules:
    # This ensures that only pushes to the default branch will trigger
    # a pages deploy
    - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
  script:
    - echo "The site will be deployed to $CI_PAGES_URL"
  tags:
    - pages

Коммит.

По прежнему считаю, что лучше снять флаг уникального домена (Deploy – Pages, вкладка Domains & settings, появится после первой успешной публикации):

pages_domain.png

В результате сайт станет доступным по адресу http://pages.local/root/pages/:

plain_html.png

Проверить можно было бы путем изменения (коммит, пуш) public/index.html, но увы, успех так и не удалось повторить.

Анализ ошибки

Что вообще происходит? gitlab-runner начинает свою работу как обычно: готовит исполнителя (docker) и окружение. Следующий шаг – загрузка исходников, он-то как раз и не отрабатывает. Повтор через 5 секунд, очистка и ошибка 500:

Getting source from Git repository
Retrying in 5s
Cleaning up project directory and file based variables
ERROR: Job failed (system failure): unable to upgrade to tcp, received 500 (exec.go:65:0s)

Предполагая, что возможно дело в версиях, остановил и отключил сервис, после чего скачал актуальный:

fetch https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/binaries/gitlab-runner-freebsd-amd64

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

./gitlab-runner-freebsd-amd64 --debug run -c /usr/local/etc/gitlab-runner/config.toml

Если что, выход по Ctrl+\. Так вот, наблюдаем такую картину:

Attaching to container dbeb9a2c1c66022a00961338d8217f93198e6981ddbd299917eeeedc77a086c9 ...  job=88 project=3 runner=qeJynmU0G
Starting container dbeb9a2c1c66022a00961338d8217f93198e6981ddbd299917eeeedc77a086c9 ...  job=88 project=3 runner=qeJynmU0G
Executing build stage                               build_stage=get_sources job=88 project=3 runner=qeJynmU0G
Getting source from Git repository      job=88 project=3 runner=qeJynmU0G

Перед выводом сообщения происходит подключение и запуск некоего контейнера. Далее следует формирование очень длинного скрипта для, собственно, загрузки исходников, после чего происходит повторная попытка подключения (attach) к этому же контейнеру – безуспешная, поскольку сразу же идет тот самый повтор через 5 секунд и далее по сути откат (archive_cache_on_failure и др).

Attaching to container dbeb9a2c1c66022a00961338d8217f93198e6981ddbd299917eeeedc77a086c9 ...  job=88 project=3 runner=qeJynmU0G
Retrying in 5s                                      job=88 project=3 runner=qeJynmU0G
Feeding runners to channel                          builds=1 max_builds=1
Feeding runner to channel                           builds=1 max_builds=1 runner=qeJynmU0G
Appending trace to coordinator...ok                 code=202 correlation_id=01K5GMG09T9AJGA9DYTMG381AY job=88 job-log=0-1226 job-status=running runner=qeJynmU0G sent-log=0-1225 status=202 Accepted update-interval=3s
Executing build stage                               build_stage=archive_cache_on_failure job=88 project=3 runner=qeJynmU0G

В /var/log/podman.log при этом видим такую запись:

time="2025-09-19T12:22:12+03:00" level=error msg="Error attaching to container dbeb9a2c1c66022a00961338d8217f93198e6981ddbd299917eeeedc77a086c9: preparing container dbeb9a2c1c66022a00961338d8217f93198e6981ddbd299917eeeedc77a086c9 for attach: plugin type=\"bridge\" failed (add): cni plugin bridge failed: failed to allocate for range 0: 10.88.0.7 has been allocated to dbeb9a2c1c66022a00961338d8217f93198e6981ddbd299917eeeedc77a086c9, duplicate allocation is not allowed"

Т.е. проблема в (пере)назначении IP-адреса контейнеру. Куда дальше копать – неясно. В принципе для Podman рекомендовали установить флаг FF_NETWORK_PER_BUILD в config.toml:

  [runners.feature_flags]
    FF_NETWORK_PER_BUILD = true

Но увы, разницы особой нет (кроме отдельного имени сети и другого IP). Я даже поднял Podman в Oracle Linux, но там естественно все работает и без этого флага. Как никак, netavark, а не CNI.

И все же – вопросов два. Контейнер dbeb9a2c1 – это кто, gitlab-runner-helper (более вероятно) или busybox (alpine)? Ну и второй – как и почему это все-таки однажды отработало?

Концепт вспомогательного образа для FreeBSD

Думая, что проблема с публикацией может быть связана с эмуляцией Linux, я решил собрать «родной» образ gitlab-runner-helper. Официальные Dockerfile’ы «очевидно» находятся в двух местах:

Из них следует, что эти образы относительно простые. Хотя наличие dump-init меня некоторым образом смущает, попробуем обойтись без него (а хотя как проверить – ничего же не работает).

Вновь констатируем, что программы написаны на go, а вместо make нужен gmake. Еще, чтобы этот самый gmake не ругался, нужны awk, bash, git и head. Как и в случае с FrankenPHP, применим многостадийную сборку: на первой стадии скомпилируем саму утилиту, а на второй уже сделаем финальный образ путем копирования файлов и установки пакетов.

ARG FREEBSD_VERSION
FROM docker.io/freebsd/freebsd-runtime:$FREEBSD_VERSION AS builder

ARG FREEBSD_VERSION GITLAB_RUNNER_VERSION

# Base OS utils
RUN set -eux; \
    cd /; \
    fetch https://download.freebsd.org/ftp/releases/amd64/amd64/${FREEBSD_VERSION}-RELEASE/base.txz; \
    tar xfm /base.txz -C / usr/bin/awk usr/bin/head usr/bin/touch usr/bin/chgrp usr/sbin/chown; \
    # cleanup
    rm base.txz

# Install build tools
RUN set -eux; \
    pkg bootstrap -y; \
    pkg install -y \
        bash \
        git \
        devel/gmake \
        lang/go; \
    # cleanup
    pkg clean -ay; \
    rm -rf /var/db/pkg/repos

# Compile gitlab-runner-helper
RUN set -eux; \
    cd /usr/src; \
    git clone -b $GITLAB_RUNNER_VERSION --depth 1 https://gitlab.com/gitlab-org/gitlab-runner.git; \
    cd gitlab-runner; \
    go telemetry off; \
    gmake helper-bin-host; \
    # smoke test
    ./out/binaries/gitlab-runner-helper/gitlab-runner-helper.freebsd-amd64 --version; \
    # cleanup
    go clean -cache -modcache


FROM docker.io/freebsd/freebsd-runtime:$FREEBSD_VERSION

# Install software
RUN set -eux; \
    pkg bootstrap -y; \
    # @todo zlib?
    pkg install -y \
        bash \
        curl \
        git \
        git-lfs \
        pcre2 \
        perl5; \
    # cleanup
    pkg clean -ay; \
    rm -rf /var/db/pkg/repos

COPY --from=builder --chmod=755 /usr/src/gitlab-runner/out/binaries/gitlab-runner-helper/gitlab-runner-helper.freebsd-amd64 /usr/bin/gitlab-runner-helper
COPY --from=builder --chmod=555 /usr/bin/touch /usr/bin/
COPY --from=builder --chmod=555 /usr/sbin/chown /usr/sbin/
COPY --chmod=755 gitlab-runner-build /usr/local/bin/

Ради нескольких файликов скачивать 200 с лишним метров базового архива – это сильно, но что поделать. chown, оказывается, есть ссылка на chgrp, поэтому сначала из архива извлекаем ее. Обратите внимание на оптимизированное клонирование исходников – я беру только конкретный тег без остальной истории коммитов. Еще один нюанс – gitlab-runner-helper должен располагаться строго в /usr/bin, в командах атлета зашит абсолютный путь. И еще нам нужен небольшой скриптик gitlab-runner-build:

#!/bin/sh
umask 0000
exec /usr/local/bin/bash

Он отличается от стандартного расположением bash и, как мне кажется, более логичной оболочкой /bin/sh.

В прошлый раз мы подняли реестр контейнеров registry.local, поэтому можно собрать образ и запушить его прямо туда:

podman build --build-arg FREEBSD_VERSION=14.3 --build-arg GITLAB_RUNNER_VERSION=v18.3.1 --tag registry.local/containers/freebsd:gitlab-runner-helper-v18.3.1 .
podman push e111076fa338 registry.local/containers/freebsd:gitlab-runner-helper-v18.3.1

Где e111076fa338 – идентификатор получившегося образа. Теперь можно переопределить helper_image в /usr/local/etc/gitlab-runner/config.toml:

  [runners.docker]
    helper_image = "registry.local/containers/freebsd:gitlab-runner-helper-v18.3.1"
    # ...

К сожалению, повторюсь, это все никоим образом не повлияет на проблему с «невозможностью апгрейда до tcp».

На всякий случай я выложил получившийся Containerfile сотоварищи у себя в GitLab: https://git.dmkos.ru/containers/freebsd/gitlab-runner-helper. Вдруг когда-нибудь в будущем концепт превратится в рабочее решение.

Заключение

Вот такая неудача постигла вашего покорного слугу, когда казалось, что вот уже все – Pages самодостаточны и во FreeBSD – но увы. Похоже что без виртуальной машины с Docker пока не обойтись (если, конечно, считать само существование GitLab во FreeBSD жизнеспособным, против чего есть весомые возражения).

Несмотря на то, что в сухом остатке процесс публикации сырого HTML (без учета создания шаблона для репозитория) сводится к нескольким командам, я пожалуй не буду пытаться делать shell или тем более кастомного палача (последний совсем какой-то сложный). Вроде бы так над страницами еще никто не издевался, за исключением, быть может, галлюцинирующего ИИ, поэтому не стоит и начинать. smile

Пока что остается лишь повторить сделанные ранее выводы. До портирования netavark и aardvark-dns или иного решения проблем с CNI Podman во FreeBSD будет оставаться белой вороной. Ждем и надеемся, надеемся и ждем. angel


Категория: Программирование, веб | Опубликовано 20.09.2025

Похожие материалы

Перенос GitLab на другой сервер в Docker

Примерно год я мучался с GitLab на сервере с двумя гигабайтами оперативки. Когда оплаченный период закончился, решил взять более мощный VDS по формуле 4/4/30. До этого сам GitLab был установлен непосредственно из репозитория, но для экспериментов с Pages и т.д. нужен Docker. А раз он и так есть, почему бы не завернуть GitLab в контейнер? Заодно на сервер можно будет установить что-нибудь еще.

Компиляция FrankenPHP во FreeBSD. Контейнер Podman

Воодушевленный созданием контейнера PHP для FreeBSD, я поставил перед собой более амбициозную цель: скомпилировать FrankenPHP. Напомню, что это веб-сервер на базе Caddy, который взаимодействует с PHP как с библиотекой и помимо этого предоставляет еще ряд различных оптимизаций. В целом все получилось, но, как говорится, есть нюансы.

Образ PHP для Podman во FreeBSD

При знакомстве с комбинацией Podman + FreeBSD я набросал Containerfile для PHP. Сейчас же я решил довести дело до ума и сделать образ, максимально приближенный к официальному в варианте FPM. Вынужден сразу предупредить, что интерпретатор, как и до этого, будет установлен с помощью пакетного менеджера, в результате невозможно будет гарантировать его точную версию.


Комментарии, обсуждение