
Практика CI/CD широко распространена в современном мире и представить ручной деплой у FAANG с их бесчисленными ежедневными изменениями просто невозможно. То же будет справедливо и для продуктовых компаний: десятки ручных деплоев в день вытянуть можно, но это потребует колоссальных ресурсов.
Освоить эту практику можно дома, в среде, где что-то сломать не страшно, ведь всегда можно начать все с самого начала. В этом гайде рассмотрим как развернуть и настроить Jenkins в Docker, как создать агента для сборки, а еще запушим образ в приватный Nexus.
Предпосылки и подготовительные работы
На этом этапе у вас уже должен быть установлен Docker for Windows, с настройкой под Linux-контейнеры и включен WSL 2 в Windows Features. Либо Linux/macOS с установленным Docker. Гайдов как это настроить достаточно много и найти их не сложно, поэтому первичная настройка Docker и WSL в этой статье описана не будет. У меня установлен Docker for Windows версии 4.10.1 и установлена WSL Ubuntu 20.04 LTS.
Отдельным абзацем отмечу, что из контейнера с Jenkins потребуется доступ к Docker daemon в котором запущен этот Jenkins контейнер. Нужно это, чтобы Jenkins мог создавать контейнеры в которых будет происходить сборка образов, так как весь процесс будет работать на одной машине. Работать это будет следующим образом: Jenkins ловит триггер по которому начинается сборка и обращается к Docker daemon в котором он сам запущен и рядом создает еще один контейнер в котором собирает Docker образ - по завершению сборки такой контейнер удалится. Для этого в настройках Docker включите следующую функцию:

Важно: это небезопасный подход к использованию Docker и делать такие настройки в корпоративных сетях не стоит, даже если это сэкономит вам деньги на использовании сервера - это снижает безопасность и фактически дает злоумышленнику удаленно выполнить код в вашем Docker daemon. В промышленной среде используйте Docker установленный на отдельном хосте с соответствующими настройками безопасности. На домашнем компьютере оставлять эту настройку включенной также не стоит: наигрались - выключите.
Для самых продвинутых
Все основные образы, контейнеры и тома я определили в docker-compose.yml, возможно он упростит запуск тестового стенда и вам, но некоторые настройки внутри Jenkins и Nexus все же придется взять из следующих шагов:
docker-compose.yml
services: jenkins: image: jenkins/jenkins:lts-jdk11 ports: - "8080:8080" - "50000:50000" volumes: - jenkins_home:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock user: root networks: - cicd_network nexus: image: sonatype/nexus3 ports: - "8081:8081" - "8083:8083" volumes: - nexus_data:/nexus-data networks: - cicd_network volumes: jenkins_home: external: true name: jenkins_home nexus_data: external: true name: nexus_data networks: cicd_network: driver: bridge
Создадим тома
Для начала выполните в PowerShell следующие команды для создания томов:
docker volume create --name jenkins_home docker volume create --name nexus_data
Эти тома будут хранить данные от контейнеров чтобы при повторном запуске не приходилось заново настраивать Jenkins и Nexus. Контейнеры способны сами создать необходимые тома, но я предпочитаю делать это заранее для пользования docker-compose.
Запуск и настройка Jenkins
Сначала запустим Jenkins и проинициализируем его. Для этого в PowerShell выполните следующую команду:
docker run -d ` --name jenkins ` --user root ` -p 8080:8080 ` -p 50000:50000 ` -v jenkins_home:/var/jenkins_home ` -v /var/run/docker.sock:/var/run/docker.sock ` jenkins/jenkins:lts-jdk11
Особо внимательные наверняка заметили, что контейнер выполняется с правами root пользователя и монтирует Unix socket. Увы это вынужденная мера чтобы контейнер смог обратиться к Docker daemon. Подробнее об этом описано в статье Docker Tips : about /var/run/docker.sock
Далее Docker попытается скачать образ jenkins/jenkins:lts-jdk11 и запустить контейнер. Если в предыдущей команде не получилось скачать образ, то выполните в PowerShell следующую команду:
docker pull jenkins/jenkins:lts-jdk11
Если все получилось, то в консоль будет выведен Id контейнера, а по адресу localhost:8080 вы увидите окно с инициализацией Jenkins:

Как вы догадались, пароль можно посмотреть по указанному выше пути, а еще зайти в консоль вывода ��онтейнера и найти следующие строки:
Jenkins initial setup is required. An admin user has been created and a password generated. Please use the following password to proceed to installation: ac8c9a1e554e4f448d46f5cdcc94477d This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
Далее выберите "Install suggested plugins" и дождитесь окончания установки. Увы, но на этом этапе мастер установки не даст выбрать плагины для работы с Docker - что иронично, интернет для их скачивания в контейнере есть, а вот поискать плагины заранее нельзя. Хотя это обходится с помощью Dockerfile для запуска Jenkins контейнера - примет вот тут.
Когда настройка завершится, будет предложено создать пользователя. Как закончите, зайдите в настройки Jenkins и перейдите в Plugin manager - там нужно установить 3 плагина:
Blue Ocean - у меня версия 1.25.5
Docker Pipeline - у меня версия 1.29
Docker plugin - у меня версия 1.2.9
Необходимые зависимости подтянутся во время установки, а по её окончанию перезапустите контейнер. Операцию можно считать успешной если после всех действий слева на главном экране появилась кнопка "Open Blue Ocean" и по её нажатию открывается окно плагина.
Теперь попробуем подключиться к Docker daemon. Для этого снова перейдите в настройки и выберите "Manage Nodes and Clouds", затем "Configure Clouds" и "Add a new cloud" - "Docker".
Тут нажмите на "Docker Cloud details" - появится несколько полей и их нужно заполнить следующими значениями:
Name - "docker"
Docker host URI - "tcp://host.docker.internal:2375" - еще можно попробовать "unix://var/run/docker.sock" и "tcp://172.17.0.1:2375", но у меня сработал только первый
Отметьте чекбоксы "Enabled" и "Expose DOCKER_HOST"
Теперь в этой же секции нажмите "Test connection". Вы поймете что подключится удалось когда слева от кнопки появится версия API:

Далее настроим агентов сборки. Для этого нажмите на "Docker Agent templates" и "Add Docker Template", а поля заполнити следующими значениями:
Labels и Name - "docker-agent"
Docker Image - "benhall/dind-jenkins-agent:v2" - если поискать, то можно найти и другие образы для использования в качестве агента сборки
Connect method - "Connect with SSH"
SSH key - "Inject SSH key"
User - "root"
Остальные поля оставьте как есть и нажмите "Save". На этом базовые настройки Jenkins можно считать завершенными, но мы вернемся к ним на этапе написания пайплайнов.
А теперь займемся Nexus
В целом з��пуск Nexus мало отличается от запуска Jenkins, так что я просто добавлю сюда PowerShell команду:
docker run -d ` --name nexus ` -p 8081:8081 ` -p 8083:8083 ` -v nexus_data:/nexus-data ` sonatype/nexus3:latest
Разумееется, если на предыдущем этапе снова не получилось скачать образ, то выполните следующую PowerShell команду:
docker pull sonatype/nexus3:latest
Запуск контейнера с Nexus занимает больше времени чем запуск Jenkins - увы красивых картинок с надписью "Подождите" не будет. Тем не мене, как только контейнер запустится, он будет доступен по localhost:8081, а localhost:8083 мы будет использовать для доступа к репозиторию образов Docker. Но сперва завершим настройку Nexus ведь при первом запуске он запросит пароль, который лежит в этом файле в томе nexus_data:

Дальше на главной странице Nexus нажмите "Sign in" и для логина admin укажите пароль из этого файла. Далее появится мастер настройки, просто следуйте по его шагам. В мастере будет пункт "Configure Anonymous Access", для себя я выбрал "Configure Anonymous Access".
Теперь по правилам хорошего тона нужно завести учетную запись для Jenkins - по ней он будет авторизоваться в приватном Docker репозитории и пушить образы, но никто не запрещает в домашней версии использовать учетную запись admin.
Для создания пользователя перейдите в настройки Nexus и выберите раздел Users. Затем нажмите "Create local user" и заполните все преложенные поля - для себя я везде ввел "jenkins". Из коробки в Nexus всего 2 преднастроенные роли, так что тут выберите nx-admin.
Теперь создадим репозиторий. Для этого в настройках выберите раздел Repositories и нажмите Create repository. В пресетах выберите "docker (hosted)", имя укажите любое на ваш вкус. На этой же странице отметьте чекбок HTTP и укажите порт 8083 - так по адресу localhost:8083 будет доступен этот репозиторий.
Стоит отметить, что прямо сейчас этот репозиторий работать не будет. Точнее будет, но Docker не сможет в нем авторизоваться. Это решается правкой Realms в одноименной секции настроек. Просто перетащите "Docker Bearer Token Realm" в правый столбец и сохраните изменения - из коробки этот Realm отключен и не зная об этом придется потратить много времени на поиск решения. Теперь Jenkins сможет авторизоваться в Nexus, а у вас успешно выполнится следующая PowerShell команда:
docker login 127.0.0.1:8083 -u jenkins -p jenkins >> Login Succeeded <<
Переходим к созданию pipeline
Для примера я сделаю пайплайн с типом "Freestyle project" - где просто попытаемся силами Jenkins собрать и запушить Docker образ в Nexus. Этот pipeline будет запускаться по кнопке, хотя можно поэксперементировать и добавить "Multibranch Pipeline", но об этом в другой раз.
Для начала на главной странице Jenkins нажмите "New item" и выберите "Freestyle project", я указал в названии "cicd_test_freestyle". В настройках pipeline нужно будет отметить чекбокс "Restrict where this project can be run" и в поле "Label expression" указать наименование агента для билда - по гайду это "docker-agent", но если вы назвали его как-то иначе, то тут будет выпадающий список с подсказками.
Далее на вкладке "Source Code Management" выберем Git укажем репозиторий из которого будем собирать Docker образ. Я для тестов использовал простенький репозиторий https://github.com/karthi4india/jenkins/ в котором буквально 2 файла: Dockerfile и http сервер.
Для аутентификации в GitHub, если репозиторий приватный, можно сначала зайти в Blue Ocean и оттуда попытаться сделать pipeline - там будет ссылка на GitHub для создания токена с нужными разрешениями. Если этот токен потом вставить в Blue Ocean, то он его запомнит, но Jenkins его не увидит и придется создать Credentials, где логин это ваш логин GitHub, а пароль - это токен.
Далее на вкладке Build в поле Cloud выберем облако "docker", которое настроили в самом начале, а в поле Image добавим 2 записи:
127.0.0.1:8083/cicd_test_freestyle:latest 127.0.0.1:8083/cicd_test_freestyle:${BUILD_NUMBER}
А еще на этой же вкладке нужно будет нажать Advanced и указать там адрес репозитория и Credentials для подключения к нему. Для меня это репозиторий 127.0.0.1:8083

Также ниже будет еще одно поле Registry Credentials, там просто укажите Credentials для Nexus. Описывать создание Credentials нет смысла, если их не хватило, то рядом всегда есть кнопка Add, а там все просто.
Так после билда в Nexus будет 2 образа: один с номером билда и второй с тегом latest, а еще сохранятся предыдущие билды:

Теперь сохраните изменения и переходите в созданный проект - он будет на главной странице Jenkins и слева нажмите "Build now". Всплывет подсказка, что билд запланирован и ниже появится агент на котором будет происходить сборка. Агент может появиться не сразу и наверняка появится сообщение "‘Jenkins’ doesn’t have label ‘docker-agent’", но через несколько секунд оно пропадет и начнется сборка Docker образа:

Самое время нажать на номер билда слева и перейти в Console Output. Если все настроили правильно, то в конце логов будет ждать приятная надпись "Docker Build Done Finished: SUCCESS", а в Nexus появятся 2 Docker образа.
Как итог
CICD на домашнем ПК собрать можно, он будет работать, билдить образы и в целом с этим очень интересно поиграться в такой песочнице. Сюда можно и нужно добавить Multibranch Pipeline для сборки по разным веткам, можно добавить и Kubernetes чтобы собранный Docker образ деплоился в daemon, но об этом уже в другой статье.
