Быстрый старт в сборке метрик по билдам в android-проекте

Привет, Хабр! Меня зовут Андрей, я новый Android-разработчик в команде онлайн-кинотеатра PREMIER. 

Когда я только пришел на работу в проект, мне поставили задачу внедрить измерение билдов для того, чтобы видеть, как с течением времени у нас меняется время сборки проекта на разных билд-машинах. 

Решение задачи

После проведенного анализа я выявил два варианта решения

  1. Использовать gradle enterprise

  2. Написать скрипты для gradle для сборки и отправки метрик

Первый вариант нам не подходит, потому что он платный;

Второй вариант нам не подходит, потому что он требует большого кол-ва времени, а в рамках испытательного срока оно ценное, и я рисковал не успеть уложиться в срок.

Так как оба варианта не подошли я начал искать альтернативное решение, которое не требовало бы много времени на написание скриптов и больших вложений. После n-го времени, проведенного в гугле и на гитхабе, нашелся один вариант, который удовлетворял запросы — это Talaiot.

204435698b9bd79585202853dd0688bd.jpeg

Немного о talaoit:
Talaiot — внешняя библиотека для gradle проектов, которая измеряет и записывает время билдов/тасок в бд. В дальнейшем эта информация помогает выявлять проблемы перфоманса тех или иных фич в проекте.

Библиотека поддерживает как локальный отчеты в формате json и визуализацию в виде картинок, так и отправку отчетов в базу данных, — расположенную на сервере. Единственный минус — это ограниченный выбор баз данных для хранения данных о сборках, в основном это time-series databases, но скорее это обусловлено работой самого talaiot.

Технологический стек:

  1. Docker-compose

  2. Grafana

  3. InfluxDB

  4. InfluxCLI

  5. Talaiot

Подготовка:

Начнем с того, что нам необходимо установить docker-compose на котором мы будет разворачивать наши контейнеры с influx-db и grafana.
Создаем папку, в которой создаем файл docker-compose.yml с описанными контейнерами.

Eсли вы более продвинутый юзер в использовании докера, никто не запрещает описать докер файл со скриптом запуска, как это сделано в примере talaiot«a на гитхабе в папке docker .


docker-compose.yml:

version:»1»

services:
  influxdb:
  image: influxdb:1.8.10
  container_name: influx_db
  environment:
 — INFLUXDB_HTTP_AUTH_ENABLED=TRUE
 — INFLUXDB_DB=metricsDB // название базы данных
 — INFLUXDB_ADMIN_USER=admin
 — INFLUXDB_ADMIN_PASSWORD=adminpass
 — INFLUXDB_WRITE_USER=influxUser
 — INFLUXDB_WRITE_USER_PASSWORD=influxPass
 — INFLUXDB_READ_USER=grafanaUser
 — INFLUXDB_READ_USER_PASSWORD=grafanaPass
 — INFLUXDB_META_DIR=/var/lib/influxdb/meta
  ports:
 — »8086:8086»

  grafana:
image: grafana/grafana-enterprise
volumes:
 — grafana-storage:/var/lib/grafana: rw
depends_on:
 — influxdb
ports:
 — »3000:3000»

volumes:
  grafana-storage:

Я создал дополнительно двух юзеров с разными правами: один записывает данные в нашу бд-шку и используется в проекте для talaiot, второй используетя графаной для чтения данных. Так-же можете можете указать параметр для каждого сервера restart: unless_stopped — это позволит контейнерам подняться самостоятельно, если удаленный сервер будет перезапущен.  Сохраняем файл и закрываем его.

Далее нам нужно поднять наши контейнеры. Открываем терминал в нашей папке и пишем заветную команду docker compose up -d и…… вуаля у нас развернуты influx-db и grafana.

84c6b8c39f1ca33c2445b51e4346284c.png


Работоспособность последнего можно проверить, перейдя по ссылке [ссылка-удаленного-или-локального-сервера]:3000, например: localhost:3000 (юзер и пароль для админки графаны по-дефолту: admin).

Influx-DB:

Для работы с моей версией influx-db необходимо поставить influx-cli (ссылки сверху). После установки пишем команду:

sudo docker exec -it influx_db /bin/bashи после вводим influx, если мы все сделали правильно, терминал должен показать сообщение:

b32c90ad4f55fb9e03f6d2aa6cc3b426.png

Теперь авторизируемся под админом в нашей базе данных, вводим следующие команды и данные, которые мы указали в docker-compose.yml

2ea872c5c479fd89d83562380914c753.png


Теперь необходимо создать retention policy, которая будет хранить данные n-ое время, я выберу срок хранения год (подробнее про retention policy  можете почитать тут)

CREATE RETENTION POLICY rpTalaiot ON metricsDB DURATION 52w REPLICATION 1

GRAFANA:

Вернемся к графане, после того, как мы авторизовались, нам необходимо связать графану и базу данных — для этого в боковом меню выбираем раздел connections → connect data

d008b27762f2e9846937edf2b9c987f4.png

В поисковой строке ищем influx и выбираем influxDb

b1b3b738a3cf12c71a346e9b440f4f57.png

В появившимся окне нажимаем по кнопке »create a influxDb data source».

Открывается окно, которое заполняем так же, как у меня на скрине, url — будет ваш локальный/удаленный url сервера, либо url докера, как в моем случае. Данные юзера берете,   которые указали в docker-compose.yml.

fe2156bcbce538cb431254860c41298d.png

Об успешности испытаний нам скажет графана:

ac0a283f6c2bcca5af4a6033f13c9670.png

Мы почти на финише, осталось только настроить проект….

Работа с проектом:

Для начала нам необходимо добавить в проект зависимости talaiot, согласно документации.

В своем примере я делаю отдельный build script для talaiot, чтобы, по возможности, можно было подключать в разные проекты. 

build.gradle (project) →

plugins {
  id «io.github.cdsap.talaiot» version »1.5.3»
}
apply from:»$rootDir/talaiot.gradle» 

Синкаем проект и описываем скрипт согласно примеру из гита


talaior.gradle →

talaiot {
publishers {
    influxDbPublisher {
        dbName = «metricsDB»
        url = http://localhost:8086

         taskMetricName = «task»
        buildMetricName = «build»
        username = «influxUser»
        password = «influxPass»
        }
}


filter {
        build {
                success = true
              // disable collect sync gradle time
                requestedTasks {
                    excludes = ['runCommonizer','prepareKotlinIdeaImport',

'prepareKotlinBuildScriptModel', 'clean']
              }
        }
}

metrics {
        gitMetrics = false
        customBuildMetrics ([buildMachine: buildMachine ()])
}
}

 А теперь вкратце объясню, что тут за что отвечает.

В publishers мы должны выбрать паблишер. Название поддерживаемой базы данных соответствует названию паблишера, у меня это influxDbPublisher.

dbName — название базы данных, которая будет создана в инфлюксе;  

url — url удаленного или локально сервера (может быть localhost или 192.168.1.8);

task и build-metricNames — я оставил без изменений;  

username и password — те что указаны в docker-compose.yml для инфлюкса.

В talaiot есть возможность отбрасывать ненужные таски, чтобы исключить их замеры. Помимо замера данных, talaiot тригерит функции гита для получения названия ветки, в которой вы работаете. Т.к. у нас есть удаленные сборщики проектов по типу миракла, там гит отсутствует, поэтому мы отключили возможность использовать сбор гит-информации. Также можно указывать свои дополнительные поля для метрик, которые будут содержать информацию, необходимую вам, для нас это модель процессора.

Для получения модели процессора был написан несложный код, который выполняется в командной строке и отбрасывает ненужную информацию необходимую вам, оставляя только семейство процессора:

static def buildMachine () {

def systems = [
        macos  : new MacHardware (),
        linux  : new LinuxHardware (),
        windows: new WinHardware ()
]

OperatingSystem os = DefaultNativePlatform.currentOperatingSystem
BaseHardware hardwareSpec = systems[os.toFamilyName ()]

if (hardwareSpec!= null) {
        return hardwareSpec.getBuildMachineName ()
} else {
          return «OS Family Unknown»
}

}

abstract class BaseHardware {
protected static String UNKNOWN_PROCESS = «Unknown process»
protected abstract String getBuildMachineInformation ()

String getBuildMachineName () {
      try {
        return parsingMachineInformation (executeProcessAndGetResult ())
      } catch (Throwable throwable) {
        throwable.printStackTrace ()
              return UNKNOWN_PROCESS
          }
}

protected abstract String parsingMachineInformation (String machineInformation)

protected String executeProcessAndGetResult () {
        ByteArrayOutputStream output = new ByteArrayOutputStream ()
        ByteArrayOutputStream error = new ByteArrayOutputStream ()

          Process process = getBuildMachineInformation ().execute ()
            process.consumeProcessOutput (output, error)
        process.waitForOrKill (5000)

    if (error.size () > 0) {
        throw new UnknownError («Cannot execute process ${error.toString ()}»)
    }
        return output.toString ()
}

}
}

class MacHardware extends BaseHardware {

@Override
protected String getBuildMachineInformation () {
        return «sysctl -n machdep.cpu.brand_string»
}

@Override
protected String parsingMachineInformation (String machineInformation) {
        return machineInformation.trim ()
}
}

class WinHardware extends BaseHardware {

@Override
protected String getBuildMachineInformation () {
        return «wmic CPU get NAME»
}

@Override
protected String parsingMachineInformation (String machineInformation) {
        return machineInformation
            .substring (4)
            .trim ()
}

}

class LinuxHardware extends BaseHardware {

@Override
protected String getBuildMachineInformation () {
        return «lscpu»
}

@Override
protected String parsingMachineInformation (String machineInformation) {
        int startIndex = machineInformation.indexOf («Model name:») + «Model      name:».length ()

        int lastIndex = machineInformation.indexOf ('\n', startIndex)
        return machineInformation.substring (startIndex, lastIndex).trim ()
}
}

c5d0a9a5a59dc0268381d831d475e578.jpeg

А, чуть не забыл, для дашборда графаны можно взять готовый sample из нашего примера и импортировать его в вашу графану. Теперь все готово к употреблению! Делаем несколько сборок проекта и проверяем результат в графане.

8004b956e381802f1e10ba056d61545a.png


Итог

Из того, что есть в открытом доступе, локально была развернута необходимая система с функционалом, которую далее можно перенести на удаленную виртуальную машину, как было сделано на нашем проекте.

Полный пример кода для android-проекта можете посмотреть в нашем примере.

P.S. Это мой первый опыт работы с докером, поэтому если Вы хотите поделиться своей идеей по оптимизации скриптов для докера — буду рад вашим комментариям!

© Habrahabr.ru