Jenkins в Docker Compose: готовый файл
Jenkins — не самый модный инструмент CI/CD, но самый живучий: тысячи плагинов, интеграция почти с любой системой контроля версий и облаком, и опыт, накопленный индустрией за пятнадцать лет. Минус у него один — ставить его «руками» на голую систему долго и хрупко: Java, свои репозитории пакетов, права на каталоги. Docker Compose снимает эту проблему: один файл поднимает мастер, сеть и volume для данных, а обновление сводится к docker compose pull && docker compose up -d. Ниже — рабочий compose-файл, который можно брать и разворачивать на VPS без дополнительных танцев.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что входит в стек
Минимальный, но production-пригодный набор:
- jenkins/jenkins:lts-jdk17 — официальный образ LTS-ветки с Java 17, самый предсказуемый вариант для продакшена;
- именованный volume под
/var/jenkins_home— здесь живут задачи, конфиги, история сборок и установленные плагины. Это единственное, что нужно бэкапить; - выделенная docker-сеть, чтобы Jenkins мог видеть reverse-proxy и, при необходимости, соседние контейнеры (например, приватный Docker Registry для публикации собранных образов);
- порты 8080 (веб-интерфейс) и 50000 (JNLP для агентов) — второй нужен, только если будете подключать выносных build-агентов, для одиночного мастера его можно не открывать наружу.
Сознательно не включаю в базовый файл docker-in-docker сайдкар — он добавляет проблем с безопасностью и правами, а нужен не всем. Вариант с ним разберу отдельным блоком ниже.
Готовый docker-compose.yml
Создайте каталог и файл:
mkdir -p /opt/jenkins && cd /opt/jenkins
nano docker-compose.yml
Содержимое:
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
container_name: jenkins
restart: unless-stopped
user: "1000:1000"
environment:
- JAVA_OPTS=-Xmx2g -Xms512m -Dhudson.model.DownloadService.noSignatureCheck=true
- JENKINS_OPTS=--prefix=/
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- jenkins_net
networks:
jenkins_net:
driver: bridge
volumes:
jenkins_home:
Пояснение по нетривиальным строкам:
user: "1000:1000"— образ по умолчанию запускается отjenkins(uid 1000), но если на хосте volume уже создан от root, придётся синхронизировать права:sudo chown -R 1000:1000 /var/lib/docker/volumes/jenkins_jenkins_home/_data;- монтирование
docker.sockдаёт мастеру возможность запускать контейнеры-агенты на лету (Docker Pipeline plugin). Это удобно, но означает, что любой, кто получит доступ к Jenkins с правами администратора задач, фактически получает root на хосте — держите это в уме и не давайте лишним людям права на создание job; JAVA_OPTSс лимитом хипа стоит подгонять под память сервера — 2 ГБ для мастера обычно с запасом, если сборки идут не на нём самом, а на выносных агентах.
Запуск:
docker compose up -d
docker compose logs -f jenkins
В логах при первом старте ищите блок вида ************* с паролем администратора — он же лежит в файле:
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПервый запуск и установка плагинов
После docker compose up -d откройте http://ваш-сервер:8080, вставьте пароль из предыдущего шага и выберите Install suggested plugins — этого достаточно для старта с Git, Pipeline и базовыми уведомлениями. Дальше донастройте под свой стек через Manage Jenkins → Plugins: Docker Pipeline, Blue Ocean (если нужен более наглядный UI пайплайнов), Credentials Binding.
Если вы разворачиваете Jenkins не один раз, а регулярно (например, отдельный инстанс под каждый проект), удобнее зафиксировать список плагинов в Dockerfile — тогда новый сервер поднимается сразу с нужным набором, без ручных кликов:
FROM jenkins/jenkins:lts-jdk17
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
plugins.txt:
git:latest
workflow-aggregator:latest
docker-workflow:latest
credentials-binding:latest
blueocean:latest
configuration-as-code:latest
Соберите свой образ и подставьте его в compose вместо jenkins/jenkins:lts-jdk17:
docker build -t my-jenkins:lts .
services:
jenkins:
image: my-jenkins:lts
# остальное без изменений
Конфигурация как код (JCasC)
Ручная настройка через веб-интерфейс плохо переживает пересоздание контейнера — весь труд живёт только в volume. Плагин Configuration as Code (уже добавлен в plugins.txt выше) позволяет описать системные настройки Jenkins в YAML и хранить его в git рядом с compose-файлом. Пример минимального jenkins.yaml:
jenkins:
systemMessage: "Jenkins настроен через JCasC"
numExecutors: 2
mode: NORMAL
security:
globalJobDslSecurityConfiguration:
useScriptSecurity: true
unclassified:
location:
url: https://ci.example.com/
Подключите файл через переменную окружения и volume:
services:
jenkins:
# ...
environment:
- CASC_JENKINS_CONFIG=/var/jenkins_home/casc/jenkins.yaml
volumes:
- jenkins_home:/var/jenkins_home
- ./casc:/var/jenkins_home/casc:ro
Теперь при старте контейнера Jenkins применяет YAML поверх текущего состояния — новый сервер с тем же репозиторием поднимается с уже готовыми настройками системы, без щёлканья по вкладкам.
Публикация через reverse-proxy и HTTPS
Открывать порт 8080 наружу без TLS — плохая идея: Jenkins передаёт логины и токены сборок. Проще всего закрыть его через Traefik, если он у вас уже настроен на сервере (подробный разбор — в статье про установку Traefik на VPS). Добавьте лейблы и уберите проброс порта 8080 наружу:
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
restart: unless-stopped
expose:
- "8080"
ports:
- "50000:50000"
labels:
- "traefik.enable=true"
- "traefik.http.routers.jenkins.rule=Host(`ci.example.com`)"
- "traefik.http.routers.jenkins.entrypoints=websecure"
- "traefik.http.routers.jenkins.tls.certresolver=letsencrypt"
- "traefik.http.services.jenkins.loadbalancer.server.port=8080"
networks:
- jenkins_net
- traefik_net
networks:
jenkins_net:
traefik_net:
external: true
Если проще поставить голый Nginx без Traefik, минимальный серверный блок с проксированием WebSocket (нужен для Blue Ocean и live-логов сборок):
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 90s;
}
Выносные build-агенты
Держать все сборки на мастере — плохая практика: он должен в первую очередь раздавать задачи и хранить состояние, а не грузить CPU компиляцией. Два рабочих варианта для Compose-инсталляции:
1. Постоянный JNLP-агент отдельным контейнером — простой вариант для небольшой команды:
jenkins-agent:
image: jenkins/inbound-agent:latest-jdk17
container_name: jenkins-agent-1
restart: unless-stopped
environment:
- JENKINS_URL=http://jenkins:8080
- JENKINS_AGENT_NAME=docker-agent-1
- JENKINS_SECRET=вставьте_секрет_из_UI
- JENKINS_AGENT_WORKDIR=/home/jenkins/agent
networks:
- jenkins_net
Секрет для агента генерируется в Manage Jenkins → Nodes → New Node (тип Permanent Agent, метод запуска — Launch agent by connecting it to the controller).
2. Динамические агенты через Docker Cloud plugin — мастер сам поднимает контейнер-агент на время сборки и удаляет после. Требует смонтированного docker.sock (он уже есть в базовом файле) и настройки Docker Cloud в Manage Jenkins → Clouds: образ агента, лейбл, лимит одновременных контейнеров. Вариант экономнее по ресурсам простоя, но сложнее в отладке — если сборка падает без логов, часто дело в правах на socket или в сетевой изоляции нового контейнера от jenkins_net.
Для проектов, где сборки сами гоняют Docker (собирают и пушат образы), удобно держать рядом приватный Docker Registry в той же docker-сети — тогда docker push registry:5000/myapp работает без выхода в интернет и без внешней авторизации.
Бэкап и обновление
Всё состояние Jenkins — в одном volume, поэтому бэкап сводится к его архивации. Остановите контейнер перед снятием снапшота (Jenkins плохо переживает бэкап «на живую», если в этот момент идёт запись индексов):
docker compose stop jenkins
docker run --rm \
-v jenkins_jenkins_home:/data \
-v /opt/backups:/backup \
alpine tar czf /backup/jenkins-$(date +%F).tar.gz -C /data .
docker compose start jenkins
Для регулярного автоматического бэкапа удобнее не городить cron-скрипты вручную, а поднять рядом Restic в отдельном compose-файле — он умеет инкрементальные снапшоты и выгрузку в S3-совместимое хранилище по расписанию.
Обновление до новой LTS-версии:
docker compose pull jenkins
docker compose up -d jenkins
Плагины после обновления образа стоит проверить отдельно — новая LTS иногда требует более свежих версий, и Jenkins сам подскажет это на странице Manage Jenkins → Plugins после рестарта. Перед крупным обновлением (сменой минорной версии LTS) лучше сначала прогнать его на копии volume — откатить контейнер просто, откатить повреждённые данные сборок сложнее.
Учётные данные и токены, которые Jenkins хранит для доступа к git-репозиториям и внешним сервисам, — отдельная тема; общие принципы их защиты в контейнерах разобраны в статье про управление секретами в Docker.
Ресурсы сервера
Сам мастер Jenkins нетребователен — координация задач и веб-интерфейс работают комфортно на 1-2 vCPU и 2 ГБ RAM. Но это только «диспетчерская»: как только вы запускаете параллельные сборки, нагрузка растёт кратно числу одновременных джобов. Это ориентир, а не измеренное значение — конкретика зависит от того, что именно вы собираете; для тяжёлых сборок закладывайте RAM с запасом, чтобы избежать OOM-килла контейнера-агента посреди сборки.
Общий подход к подбору конфигурации под CI/CD-нагрузку — в статье сколько ресурсов нужно VPS для разработчика и CI/CD. Если начинаете с малого — мастер и лёгкие сборки можно держать на одном сервере среднего тарифа, а под тяжёлые проекты вынести build-агентов на отдельный сервер и подключить их по JNLP через защищённый канал.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Почему Jenkins не стартует и в логах ошибка доступа к /var/jenkins_home?
Обычно это несовпадение прав — volume создан от root, а процесс в контейнере работает от uid 1000. Исправьте через chown -R 1000:1000 на каталог volume или явно уберите user: "1000:1000" из compose, если готовы, чтобы контейнер работал от root.
Можно ли монтировать docker.sock, если сервер общий с другими сервисами?
Технически да, но это равносильно выдаче root-доступа к хосту всем, кто может создавать job в Jenkins. На общем сервере с чужими сервисами лучше вынести Jenkins и его агентов на отдельный VPS или использовать динамические агенты в изолированной сети без доступа к сокету хоста.
Как перенести Jenkins на другой сервер?
Остановите контейнер, заархивируйте volume (см. раздел про бэкап), перенесите архив на новый сервер и распакуйте в volume с тем же именем перед первым запуском. Общие шаги переноса Docker-проектов между серверами разобраны отдельно — перенос Docker-проекта на другой сервер.
Нужен ли отдельный volume под workspace сборок?
Не обязательно — по умолчанию workspace живёт внутри jenkins_home/workspace. Если сборки генерируют много временных файлов и это раздувает основной volume, вынесите workspace отдельным volume или bind-mount на отдельный диск — так проще чистить его без риска задеть конфиги и историю.
Что делать, если после обновления образа плагины конфликтуют по версиям?
Откатитесь на предыдущий тег образа (docker compose up -d с прежним image: в файле), обновите проблемные плагины вручную до совместимых версий через UI, и только после этого повторите обновление ядра.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →