Привіт, це Kernelka! Сьогодні покажу, як швидко підняти локальний GitLab Runner у Docker на Linux, щоб автоматично збирати й тестувати ваші проєкти на Python та Node.js. Буде мінімум магії, максимум практики і трохи турботи про продуктивність 🚀

Що ви отримаєте

  • Працюючий GitLab Runner у контейнері (Docker на Linux)
  • Зразок .gitlab-ci.yml для тестів і збірки python в Linux і nodejs в Linux
  • Кешування залежностей, артефакти та пара порад для стабільної контейнеризація

Передумови

  • Linux-машина з доступом до інтернету
  • Встановлений Docker (root або користувач у групі docker)
  • Проєкт у GitLab і доступ до його налаштувань (щоб взяти токен для Runner)

Основний How-to

Крок 1. Встановіть Docker

Найшвидше — офіційний інсталер. Відкрийте термінал і виконайте:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker

Перевірте, що все ок:

docker run --rm hello-world

Крок 2. Запустіть контейнер GitLab Runner

Створимо директорію для конфігів і піднімемо контейнер з монтуванням Docker-сокета (щоб job-и могли запускати контейнери):

sudo mkdir -p /srv/gitlab-runner/config
sudo docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:alpine

Подивіться логи на старт:

docker logs -f gitlab-runner

Крок 3. Реєстрація Runner у GitLab

У GitLab відкрийте ваш проєкт: Settings → CI/CD → Runners → Registration token. Потім у терміналі виконайте:

docker exec -it gitlab-runner gitlab-runner register

Відповідайте на запитання:

  • Enter the GitLab instance URL: https://gitlab.com (або URL вашого self-hosted GitLab)
  • Enter the registration token: Поставте ваш токен
  • Description: local-docker-runner
  • Tags: docker,linux,python,node
  • Executor: docker
  • Default Docker image: ubuntu:22.04 (або будь-який базовий образ)

За потреби можна вручну підкрутити конфіг:

# /srv/gitlab-runner/config/config.toml
concurrent = 2
[session_server]
  session_timeout = 1800

[[runners]]
  name = "local-docker-runner"
  url = "https://gitlab.com/"
  token = "REDACTED"
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "ubuntu:22.04"
    privileged = false
    pull_policy = "if-not-present"
    shm_size = 0
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]

Примітка: якщо у job-і треба будувати Docker-образи, або запускати в Docker-in-Docker, увімкніть privileged = true для цього runner або використовуйте сервіс docker:dind (див. альтернативи нижче).

Крок 4. .gitlab-ci.yml для Python і Node.js

Приклад з окремими job-ами, кешем залежностей і артефактами тестів.

stages:
  - test
  - build

.default_cache: &default_cache
  key: "${CI_COMMIT_REF_SLUG}"
  policy: pull-push

variables:
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  PIP_DISABLE_PIP_VERSION_CHECK: "1"
  NPM_CONFIG_CACHE: "$CI_PROJECT_DIR/.npm"

python:test:
  stage: test
  image: python:3.11-slim
  cache:
    <<: *default_cache
    paths:
      - .cache/pip/
  before_script:
    - python --version
    - python -m pip install --upgrade pip
    - |
      if [ -f requirements.txt ]; then \
        pip install -r requirements.txt; \
      fi
  script:
    - |
      if [ -f pytest.ini ] || [ -d tests ]; then \
        pip install pytest; \
        pytest --maxfail=1 --disable-warnings -q --junitxml=pytest-report.xml; \
      else \
        echo "No tests found"; \
      fi
  artifacts:
    when: always
    paths:
      - pytest-report.xml
    reports:
      junit: pytest-report.xml

node:test:
  stage: test
  image: node:20
  cache:
    <<: *default_cache
    paths:
      - .npm/
      - node_modules/
  before_script:
    - node -v
    - npm -v
    - |
      if [ -f package-lock.json ]; then \
        npm ci; \
      elif [ -f package.json ]; then \
        npm install; \
      fi
  script:
    - |
      if npm run | grep -q " test"; then \
        npm test --silent || npm test; \
      else \
        echo "No npm test script"; \
      fi
  artifacts:
    when: always
    paths:
      - coverage/

node:build:
  stage: build
  image: node:20
  needs: ["node:test"]
  cache:
    <<: *default_cache
    paths:
      - .npm/
      - node_modules/
  script:
    - |
      if npm run | grep -q " build"; then \
        npm run build; \
      else \
        echo "No build script"; \
      fi
  artifacts:
    paths:
      - dist/

Цей пайплайн запускає тести для Python і Node.js, кешує pip та npm, зберігає артефакти та результати тестів.

Альтернативні способи

Shell executor (без контейнерів)

Можна зареєструвати runner з executor=shell. Плюс — мінімальні накладні витрати; мінус — вам треба самостійно тримати на хості всі потрібні версії Python/Node.js. Використовуйте лише на виділених машинах.

Docker executor vs Docker-in-Docker

  • Docker executor з монтуванням /var/run/docker.sock — швидкий і простий для більшості CI (тести, збірка артефактів).
  • Docker-in-Docker (служба docker:dind + privileged) — коли потрібно будувати Docker-образи всередині job-ів. Приклад фрагмента:
build:image:
  stage: build
  image: docker:24.0
  services:
    - name: docker:24.0-dind
      command: ["--tls=false"]
  variables:
    DOCKER_HOST: tcp://docker:2375
    DOCKER_DRIVER: overlay2
  script:
    - docker build -t myapp:${CI_COMMIT_SHORT_SHA} .
    - docker save myapp:${CI_COMMIT_SHORT_SHA} -o image.tar
  artifacts:
    paths:
      - image.tar

Podman як альтернатива Docker

Якщо ви живете у Red Hat/Fedora-світі — розгляньте Podman. GitLab Runner підтримує його через docker-compat режим, але уважно тестуйте, бо деякі edge-кейси відрізняються.

GUI-спосіб

  • У GitLab зайдіть у ваш проєкт → Settings → CI/CD → Runners → Expand. Скопіюйте Registration token (GUI-частина найважливіша тут).
  • Для керування контейнерами з GUI можна використати Portainer: запустіть portainer/portainer-ce у Docker і додавайте/перезапускайте Runner одним кліком.
  • Логи job-ів дивіться у вкладці Pipelines в інтерфейсі GitLab. Там же видно артефакти та звіти тестів.

FAQ

Runner зареєстровано, але job-и «pending»

Переконайтеся, що теги збігаються. Якщо у job-і зазначені теги, а у runner — інші, GitLab не віддасть завдання. Можна тимчасово прибрати теги з job-у для перевірки.

permission denied: /var/run/docker.sock

Для контейнеризованого Runner обов’язково монтуйте сокет Docker і не забороняйте доступ:

# Перезапуск з правильними volume
sudo docker rm -f gitlab-runner
sudo docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:alpine

docker:dind не стартує або не будує образ

  • Додайте privileged = true для runner у config.toml або використовуйте services: docker:dind з --tls=false.
  • Переконайтесь у відповідності версій образів docker: клієнт і dind повинні збігатися.

Кеш не працює/не пришвидшує збірку

  • Перевірте cache:key (реком. ${CI_COMMIT_REF_SLUG} або стабільний ключ для main).
  • Правильно вказуйте шляхи: .cache/pip/, .npm/, node_modules/.

Де дивитися логи Runner?

docker logs -f gitlab-runner

Також у GitLab → Pipelines → конкретний job → вкладка Log.

Порада від Kernelka

Тримайте runner «тонким»: образи для job-ів задавайте у .gitlab-ci.yml, а не як «default image» для всього runner. Так легше оновлювати середовища під конкретні пайплайни і уникати дивних конфліктів залежностей. І не соромтеся додавати маленькі smoke-тести — швидкі перевірки ловлять великі проблеми 😉

Підсумок

  • Підняли GitLab Runner у Docker на Linux і зареєстрували його в GitLab
  • Налаштували CI для python в Linux і nodejs в Linux із кешем та артефактами
  • Розібрали альтернативи: shell executor, Docker-in-Docker, Podman
  • Отримали відповіді на типові помилки та чекліст стабільності