Практика 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, но об этом уже в другой статье.