- PVSM.RU - https://www.pvsm.ru -
Всем привет!
Я работаю андроид разработчиком, и не так давно мы столкнулись с некоторыми рутинными задачами на своем проекте, которые хотелось бы автоматизировать. Например у нас 5 разных flavor, для каждого из которых требуется загружать свой билд на fabric, иногда для разных тасок по несколько раз в день. Да эту задачу можно сделать и с помощью gradle таски, но хотелось бы не запускать этот процесс на машине разработчика, а делать это как-то централизовано. Или например автоматически заливать билд в google play в бету. Ну и просто хотелось поковырять CI систему. Что из этого получилось, и как мы это настраивали, зачем там Docker, далее в статье.
В моем понимании вся задача делилась примерно на два этапа:
В этой статье я хочу затронуть именно первый пункт, а если это все будет кому-то интересно, то в следующей статье опишу и процесс настройки задач по сборке в самом Jenkins.
На Хабре уже есть замечательная статья [1] на эту тему, но ей уже пару лет, и некоторые вещи в ней слегка устарели (например sdkmanager), хотя она мне сильно помогла разобраться на начальных этапах что и как делать.
Если посмотреть официальную документацию [2] по установке Jenkins то увидим три разных способа как это сделать: запустить готовый docker образ, скачать и запустить war файле, а также по старинке просто установить jenkins в систему (например apt-get install jenkins
на примере ubuntu). Первый вариант самый правильный, поскольку он не несет никаких лишних настроек и зависимостей в нашу хост систему, и в любой момент даже если что-то пойдет не так, легко и просто все удалить и начать заново. Но стандартный docker образ для jenkins содержит часть данных которые нам не нужны (например blueocean плагин ) и не содержат того что нам обязательно понадобиться (например android sdk). Было принято решение создать собственный docker образ который внутри себя будет качать и запускать war файл, качать и устанавливать android sdk, а также настраивать все остальные настройки которые нам будут нужны. Для того чтоб его потом запустить, нам потребуеться хостовая система с установленным docker. Я предлагаю тут не изобретать велосипед и воспользоваться DigitalOcean.
Для начала если там еще кто не зарегистрирован то предлагаю зарегистрироваться [3](тут в момент написания статьи была реферальная ссылка, но почитав правила я выкинул ее). После регистрации можно на просторах инета погуглить тот или иной промокод, и получить примерно баксов 10 для старта.
После нам потребуется завести новый дроплет. Выберем пункт Droplets, и далее Create Droplet.
Хостовой системой оставим Ubuntu 18.04. Можно было бы выбрать образ с уже установленным и настроенным Docker, но мы все сделаем самостоятельно. Поскольку сборка андроид билдов дело все же ресурсоемкое, нам нужно выбрать конфигурацию как минимум за 20 баксов, чтоб билды собирались нормально и относительно быстро.
Выберем расположение к себе поближе (например в Германии). Дальше два варианта как мы будем подключаться к нашему виртуальному серверу. Мы можем добавить ssh ключ или обойтись без него. Если в этом месте мы не укажем какой ключ использовать, то нам на почту придет пароль для юзера root.
Тут можем изменить имя сервера, и завершаем создание нажатием кнопки Create.
Теперь заходим в созданный дроплет, и копируем себе ip адрес, для дальнейшего подключения и настройки.
Нам нужен ssh клиент. Если вы работает из под мака, то можно воспользоваться стандартным терминалом, если из под винды то мы можем взять для работты putty [4] или воспользоваться подсистемой Linux [5] (только для Windows 10). Я лично использую последний вариант, и он меня полностью устраивает.
Подключаемся к нашему серверу с помощью следующей команды
ssh root@YOUR_IP_ADDRESS
Консоль вам предложить сохранить ключ, соглашаемся с этим. После подключения создадим себе нового пользователя, добавим его в суперпользователи (и дадим ему возможность пользоваться sudo без пароля), копируем ему ключ для доступа по ssh и скажем что он владелец этих файлов (иначе не заработает). Имя username меняем на любое удобное для вас.
useradd -m -s /bin/bash username
&& echo 'username ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
&& mkdir /home/username/.ssh
&& cp /root/.ssh/authorized_keys /home/username/.ssh/authorized_keys
&& chown username:username -R /home/username/.ssh
Отключаемся от пользователя root с помощью команды
exit
И подключимся заново уже с помощью нами созданного нового пользователя
ssh username@YOUR_IP_ADDRESS
После обновим систему, и перезагружаем наш сервер (если в процессе обновления система у вас будет что-то спрашивать, достаточно в этом случае всегда выбирать дефолтные значения).
sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y && sudo reboot
Базовая настройка закончена. С точки зрения боевой системы она не очень секьюрна, но в рамках этой статью полностью подойдет.
Чтоб установить Docker в нашу систему воспользуемся официальной документацией [6]. Поскольку у нас заново установленная система, мы пропустим этот пункт, а если у вас система на которой уже давно что-то бежит, по рекомендации ребят из Docker удалите возможные старые версии
sudo apt-get remove docker docker-engine docker.io containerd runc
Не забудьте вначале обратно подключиться по ssh к нашему серверу. Сама установка Docker расписана очень детально в документации, я приведу общие команды, чтоб вам было проще. Что они делают можно почитать там. Сначала добавим репозиторий.
sudo apt update
&& sudo apt install -y apt-transport-https ca-certificates
curl gnupg-agent software-properties-common
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
&& sudo apt-key fingerprint 0EBFCD88
&& sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
И после установим сам Docker:
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io
Чтоб в дальнейшем мы могли вызывать команды docker без приставки sudo выполним следующую команду (котрая тоже заботливо описана в инструкции [7]).
sudo usermod -aG docker username
После нужно перезайти (с помощью команды exit и повторного подключения к серверу) для того чтоб изменения закрепились.
Сам Docker установлен, что мы можем проверить командой
docker run hello-world
Она загружает тестовый образ, запускает его в контейнере. Контейнер после запуска печатает информационное сообщение и завершает работу.
Поздравляю, этап подготовки сервера к работе мы закончили!
Создавать Docker образ будем с помощью написания собственного Dockerfile. Примеров как это сделать правильно в интернете вагон и маленькая тележка, я покажу свой [8] уже готовый вариант, и постараюсь его максимально прокомментировать. Существует также от самого docker статья инструкция [9] с примерами по правильному и каноническому написание dockerfile.
Создадим и откроем для редактирования свой Dockerfile
touch Dockerfile && nano Dockerfile
В него для примера, поместим содержимое моего Dockerfile
# базовая система для образа.
FROM ubuntu:18.04
# тут должно быть все понятно
LABEL author="osipovaleks"
LABEL maintainer="osipov.aleks.kr@gmail.com"
LABEL version="1.0"
LABEL description="Docker image for Jenkins with Android SDK"
# устанавливаем таймзону, чтоб Jenkins показывал локальное время
ENV TZ=Europe/Kiev
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
#добавляем i386 архитектуру для установки ia32-libs
RUN dpkg --add-architecture i386
# обновляем пакеты и устанавливаем нужное
RUN apt-get update && apt-get install -y git
wget
unzip
sudo
tzdata
locales
openjdk-8-jdk
libncurses5:i386
libstdc++6:i386
zlib1g:i386
#чистим после себя, чтоб размер образа был немного поменьше
RUN apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt
#устанавливаем локали
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
#качаем и распаковываем Android Sdk в заранее подготовленную папку
ARG android_home_dir=/var/lib/android-sdk/
ARG sdk_tools_zip_file=sdk-tools-linux-4333796.zip
RUN mkdir $android_home_dir
RUN wget https://dl.google.com/android/repository/$sdk_tools_zip_file -P $android_home_dir -nv
RUN unzip $android_home_dir$sdk_tools_zip_file -d $android_home_dir
RUN rm $android_home_dir$sdk_tools_zip_file && chmod 777 -R $android_home_dir
#устанавливаем environment в наш образ
ENV ANDROID_HOME=$android_home_dir
ENV PATH="${PATH}:$android_home_dir/tools/bin:$android_home_dir/platform-tools"
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
#соглашаемся с лицензиями Android SDK
RUN yes | sdkmanager --licenses
#создаем рабочую директорию для Jenkins
ENV JENKINS_HOME=/var/lib/jenkins
RUN mkdir $JENKINS_HOME && chmod 777 $JENKINS_HOME
#заводим нового юзера с именем jenkins, сделаем его суперпользователем, переключимся на него и перейдем в рабочую директорию
RUN useradd -m jenkins && echo 'jenkins ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER jenkins
WORKDIR /home/jenkins
#загрузим и запустим war файл с последней версией Jenkins
RUN wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war -nv
CMD java -jar jenkins.war
#сообщим какой порт нам требуется слушать
EXPOSE 8080/tcp
Несколько уточнений:
sdkmanager --list
позволяет просмотреть все доступные и все установленные компоненты, а sdkmanager --install "platforms;android-26"
установит SDK для 26 версии api.После окончания написания Dockerfile нам нужно его превратить в готовый образ для Docker, на основании которого и будут создаваться контейнеры. Делается это просто командой
docker build -t jenkins-image
где параметр -t jenkins-image
отвечает за имя вашего образа, а точка в конце команды, говорит о том что Dockerfile для сборки нужно искать внутри данного каталога. Сам процесс сборки занимает какое-то время, и после сборки в консоли должно быть примерно подобное сообщение.
Successfully built 9fd8f5545c27
Successfully tagged jenkins-image:latest
Которое нам говорит о том что наш образ успешно собран, и мы можем приступать к следующему шагу, а именно запуску нашего контейнера
Да конечно, мы можем использовать наш готовый образ для запуска контейнера, но если нам потребуется это сделать больше чем на нескольких устройствах, каждый раз создавать Dockerfile и собирать из него готовый образ будет не совсем удобно. А если мы еще и обновим содержимое нашего Dockerfile, то раскатывать изменения по всем нодам будет вообще не удобно. Для этих целей и существует публичный репозиторий образов Docker Hub [11]. Он позволяет не собирать каждый раз образ, на каждой ноде, а просто скачать его себе с публичного хранилища, и использовать одинаково на всех машинах. Например образ который послужил примером для этой статьи, доступен в репозитории по имени osipovaleks/docker-jenkins-android [12], и дальше в статье мы будем работать именно с ним.
Данная статья не подразумевает детального изучения Docker Hub, мы не будем разбираться как залить туда свои образы (хотя это очень не сложно) и что с ними можно делать дальше там, мы не будем разбираться что существуют еще могут быть свои личные публичные или приватные репозитории, в этом всем можно будет разобраться самостоятельно если понадобиться.
Запустить контейнер можно двумя способами.
docker run
, позволяет это сделать легко и быстро следующим способом
docker run --name jenkins -d -it -v jenkins-data:/var/lib/jenkins -v jenkins-home:/home/jenkins -p 8080:8080 --restart unless-stopped osipovaleks/docker-jenkins-android
где команда run
имеет следующие параметры
--name jenkins
— имя будущего контейнера-d
— запуск контейнера в фоне-it
— флаги для работы с STDIN и tty-v jenkins-data:/var/lib/jenkins
и -v jenkins-home:/home/jenkins
— создаем (если не созданы) и замапим на внутренние разделы контейнера специальный файлы тома, которые позволят нам сохранить наш настроенный Jenkins даже после пересоздания контейнера-p 8080:8080
— замапим порт хоста на порт контейнера, для того чтоб у нас был доступ к веб интерфейсу (да это именно тот порт который мы указывали в Dockerfile)--restart unless-stopped
— опция определяет политику автозапуска контейнера после перезагрузки хоста (в данном случае, автостарт если контейнер не был выключен вручную)osipovaleks/docker-jenkins-android
— образ для развертывания.На выходе в консоль Docker нам должен вывести id созданного контейнера, а также показать информацию о том как образ загружается в систему (конечно если он еще не загружен), примерно так
Unable to find image 'osipovaleks/docker-jenkins-android:latest' locally
latest: Pulling from osipovaleks/docker-jenkins-android
6cf436f81810: Pull complete
987088a85b96: Pull complete
b4624b3efe06: Pull complete
d42beb8ded59: Pull complete
b3896048bb8c: Pull complete
8eeace4c3d64: Pull complete
d9b74624442c: Pull complete
36bb3b7da419: Pull complete
31361bd508cb: Pull complete
cee49ae4c825: Pull complete
868ddf54d4c1: Pull complete
361bd7573dd0: Pull complete
bb7b15e36ae8: Pull complete
97f19daace79: Pull complete
1f5eb3850f3e: Pull complete
651e7bbedad2: Pull complete
a52705a2ded7: Pull complete
Digest: sha256:321453e2f2142e433817cc9559443387e9f680bb091d6369bbcbc1e0201be1c5
Status: Downloaded newer image for osipovaleks/docker-jenkins-android:latest
ef9e5512581da66d66103d9f6ea6ccd74e5bdb3776747441ce6a88a98a12b5a4
Для этого нам потребуется установить его:
sudo apt update && sudo apt install -y docker-compose
Далее создаем директорию для проекта (это важно в том случае, если вам не все равно, как будут называться автоматически созданные volumes для контейнера) и перейдем в нее
mkdir jenkinsProject && cd jenkinsProject
а внутри создаем сам compose файл и заходим в режим редактирования
touch docker-compose.yml && nano docker-compose.yml
и поместим в него следующее содержимое
version: '3'
services:
jenkins:
container_name: jenkins
image: osipovaleks/docker-jenkins-android
ports:
- "8080:8080"
restart: unless-stopped
volumes:
- "jenkins-data:/var/lib/jenkins"
- "jenkins-home:/home/jenkins"
volumes:
jenkins-data:
jenkins-home:
В нем, пожалуй, только первая строчка вызывает вопросы (version: '3'
) которая указывает на версию [14] возможностей compose файла, а также раздел с блоком volumes
в котором перечислены те, которые используються в данном контейнере
Запустим свой контейнер командой:
docker-compose up -d
где флаг -d
также указывает на то что создание и запуск контейнера будет производиться в фоне. В итоге Docker должен показать примерно следующее:
Creating volume «jenkinsproject_jenkins-data» with default driver
Creating volume «jenkinsproject_jenkins-home» with default driver
Pulling jenkins (osipovaleks/docker-jenkins-android:latest)…
latest: Pulling from osipovaleks/docker-jenkins-android
6cf436f81810: Pull complete
987088a85b96: Pull complete
b4624b3efe06: Pull complete
d42beb8ded59: Pull complete
b3896048bb8c: Pull complete
8eeace4c3d64: Pull complete
d9b74624442c: Pull complete
36bb3b7da419: Pull complete
31361bd508cb: Pull complete
cee49ae4c825: Pull complete
868ddf54d4c1: Pull complete
361bd7573dd0: Pull complete
bb7b15e36ae8: Pull complete
97f19daace79: Pull complete
1f5eb3850f3e: Pull complete
651e7bbedad2: Pull complete
a52705a2ded7: Pull complete
Digest: sha256:321453e2f2142e433817cc9559443387e9f680bb091d6369bbcbc1e0201be1c5
Status: Downloaded newer image for osipovaleks/docker-jenkins-android:latest
Creating jenkins…
Creating jenkins… done
Помните я говорил что от имени проекта будет зависеть имя созданных volumes? Выполним команду:
docker volume ls
и получим на выходе такое
DRIVER VOLUME NAME
local jenkinsproject_jenkins-data
local jenkinsproject_jenkins-home
где и увидим, что несмотря на то что имя для volume было выбрано jenkins-home
, в реальности к нему прилепился префикс из имени проекта и имя volume получилось jenkinsproject_jenkins-home
Какой из вариантов запуска использовать? Тут вы можете выбрать самостоятельно, считается что Docker Compose это скорее инструмент, для запуска нескольких контейнеров сразу, которые завязаны друг на друга, и если вам нужно запустить всего один контейнер, то можно воспользоваться просто командой docker run
.
Теперь после этих подэтапов по запуску и настройке сервера, а так же запуске контейнера с Jenkins мы можем перейти к его первоначальной настройке
Возьмем ip адрес нашего сервера, добавим к нему указанный нами порт 8080 и перейдем по этой ссылке в браузере.
http://YOUR_IP_ADDRESS:8080/
Если до этого все было настроено и запущено правильно, то тут увидим следующую картинку
Для первой настройки нам нужно ввести пароль который сгенерировала система при установке. Для этого нам всего лишь нужно посмотреть содержимое файла /var/lib/jenkins/secrets/initialAdminPassword
. Но этот файл находиться внутри нашего запущенного контейнера, и для того чтоб его прочесть, нам понадобиться приконектиться к контейнеру, с помощью следующей команды:
docker exec -it jenkins /bin/bash
где параметр -it
аналогичен как и при запуске docker run
, jenkins
это имя нашего контейнера, а /bin/bash
запустит для нас bash в контейнере и даст к нему доступ. После этого мы можем посмотреть начальный пароль для Jenkins:
cat /var/lib/jenkins/secrets/initialAdminPassword
в консоли покажеться примерно следующее
91092b18d6ca4492a2759b1903241d2a
Это и есть пароль. Копируем его, вставляем в поле Administrator password в веб интерфейсе и нажимаем Continue. На следующем экране выбираем пункт Install suggested plugins и устанавливает набор дефолтных плагинов.
После установки плагинов, создаем себе пользователя и нажимаем на Save and Finish
Соглашаемся с разделом Instance Configuration, где нам предлагают заполнить URL на котором будет работать Jenkins (в нашем случае оставляем все как есть)
И на следующем экране нажимаем заветное Start using Jenkins
Итак, мы установили и запустили Jenkins!
С ним уже вполне можно работать, но для того чтоб собирать наши Android билды нужно будет настроить еще несколько пунктов. Локализация Jenkins связана с выбранным языком вашего браузера, и естественно перевод на русский язык закончен не до конца, и мы получаем адовую смесь из русского и английского языков. Если у вас получилось точно также, и вас это бесит, то можно воспользоваться специальным плагином [15] и установить дефолтный язык интерфейса. Ну или переключить свой браузер на англоязычный интерфейс.
Перейдем в настройки Jenkins и выберем пункт Конфигурация системы
Отметим галочкой пункт Environment variables, и впишем в поле имя ANDROID_HOME, а в поле значение укажем /var/lib/android-sdk/ (именно эти данные мы указывали еще в Dockerfile как домашнюю директорию для хранения Android SDK).
Нажмем на кнопку Сохранить, выйдем из данного раздела настроек и зайдем в раздел под названием Конфигурация глобальных инструментов.
Настроем раздел с JDK (где переменная JAVA_HOME так же заполнялась нами в Dockerfile, и мы можем тут использовать ее значение /usr/lib/jvm/java-8-openjdk-amd64/).
Так же тут нам еще нужно заполнить раздел с Gradle. Версию Gradle мы выбираем и устанавливаем ту, которая используется в тех проектах, которые вы будете собирать с помощью этой CI системы. Можно завести несколько версий. Можно так же вообще не заводить Gradle переменную, если у вас в репозитории например есть gradlew, и собирать можно с его помощью.
На этом мы можем заканчивать наш первый этап. Система Jenkins полностью готова к работе и мы можем переходить к настройке самих задач по сборке. Обратите внимание, система настраивалась под наши нужды и тут может не оказать того что нужно именно вам — например тут нету андроид эмуляторов для тестирования и NDK.
Если эта статья кого-то заинтересует, то я продолжу и во второй части на примере одной или двух тасок, опишу интеграцию Jenkins и Bitbucket (именно он, а не Github, потому что там и с бесплатными приватными репозиториями попроще, и статей в интернете про него поменьше, а приколов пожалуй побольше), расскажу как ssh ключ нашего контейнера подружить с репозиторием, про email уведомления, а также несколько других фишек. В общем примерно про все, что у нас и настроено.
Прошу сильно не пинать, это моя первая статья на Хабр. Всем добра!
Автор: osipovaleks
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/311512
Ссылки в тексте:
[1] статья: https://habr.com/ru/post/315804/
[2] документацию: https://jenkins.io/doc/book/installing/
[3] зарегистрироваться : https://cloud.digitalocean.com/registrations/new
[4] putty: https://www.putty.org/
[5] подсистемой Linux: https://ru.wikipedia.org/wiki/Windows_Subsystem_for_Linux
[6] официальной документацией: https://docs.docker.com/install/linux/docker-ce/ubuntu/
[7] инструкции: https://docs.docker.com/install/linux/linux-postinstall/
[8] свой: https://github.com/osipovaleks/docker-jenkins-android/blob/master/Dockerfile
[9] инструкция: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
[10] ia32-libs: https://github.com/gliderlabs/docker-alpine/issues/169
[11] Docker Hub: https://hub.docker.com
[12] osipovaleks/docker-jenkins-android: https://cloud.docker.com/u/osipovaleks/repository/docker/osipovaleks/docker-jenkins-android
[13] YAML: https://ru.wikipedia.org/wiki/YAML
[14] версию: https://docs.docker.com/compose/compose-file/compose-versioning/
[15] плагином: https://wiki.jenkins.io/display/JENKINS/Locale+Plugin
[16] Источник: https://habr.com/ru/post/443606/?utm_campaign=443606
Нажмите здесь для печати.