31 июл. 2015 г.

Jenkins скушал логами все свободное место на диске?

На VPS'ке (Digital Ocean для Continuous Integration! Gitlab + Jenkins [tutorial]) появилась проблема: за несколько часов ночью jenkins зафлудил логами все свободное место на диске. Решается добалвением в файл /etc/default/jenkins агрумента "-Dhudson.DNSMultiCast.disabled=true" в переменную JAVA_ARGS.
В моей файле JAVA_ARGS стал выглядеть так:

JAVA_ARGS="-Djava.awt.headless=true -Dhudson.DNSMultiCast.disabled=true"

11 мая 2015 г.

Digital Ocean для Continuous Integration! Gitlab + Jenkins [tutorial].

В седые времена на настройку команды разработчиков уходило много времени и сил. Мало было просто поселить в голову отдельного разработчика идеи о TDD и его пользе. Ещё нужны были администраторы с руками растущими из правильных мест (ну это те, у которых кофта заправлена в джинсы и на лице густая борода), и нужно они были не один разок, а постоянно. Сейчас все иначе. Имея базовые знания можно, развернуть весь continuous integration (CI) не открываясь от смузи.

Итак, нам потребуется: $10 в месяц на оплату собственного VPS (на нем же можно хостить MVP вашего стартапа, и еще много всякого - например VPN-шлюз для маскировки под американскую компанию), 2 опсосных проекта и некоторое количество свободного времени.

Gitlab - это такой открытый Github у вас на сервере. Можно конечно обойтись Bitbucket'ом с закрытой группой (team), но это во первых не круто =), а во-вторых, стартап должен параноидально охранять свою супер новую уникальную социальную сеть "Водноклассников Мир".

Jenkins - инструмент непрерывной интеграции, написанный на Java. Есть много разных альтернатив, но вот что нужно запомнить про них про все (субъективно):
  • если инструмент имеет продвинутый функционал - он написан на java
  • если инструмент написан не на java, скорее всего из коробки он умеет очень мало, а настройка всего дополнительного - боль
  • настройка любых метрик в любой CI - это тоже боль
  • каждая команда имеет свой совершенно уникальный процесс разработки, нет двух одинаковых CI-процессов
  • каждая команда стремится создать свой уникальный deploy-процесс, но автоматизировать нужно только то, в чем админы реально не принимают никакого участия (читай: и не несут никакой ответственности). Выкладка новой версии внутренней python-библиотеки в закрытый репозиторий компании - хороший пример такой работы
  • и самое главное: невозможно один раз настроить CI и никогда больше к нему не возвращаться. Поддержание работоспособности CI - это отдельная настоящая работа, которой тоже нужно заниматься. Постоянно. Правда, всё потраченное время и силы вернутся сторицей.
Digital Ocean (DO) - очень модный продавец VPS'ок, который держит нос исключительно "по ветру". Цены у них может быть и не самые низкие, но: API, хранение и дистрибуция слепков, куча дата-центров по всему миру, и настройка в один клик - безусловно доставляют.

Теперь о том, как все это соединить без напрягов.

1. Создаем сервер с GitLab на борту при помощи one-click-install
Не вижу никакого смысла повторять или переводить туториал написанный DO: How To Use the GitLab One-Click Install Image to Manage Git Repositories.
DO не рекомендует устанавливать Gitlab на дроплет за 5 долларов - слишком уж мало там памяти. $10 - будет вполне достаточно. Но если очень хочется сэкономить, можно просто увеличить swap - для машинки с такими утилитарными задачами LA не критичен, как по мне. При заходе на one-click install с регистрации вам не дадут использовать $5 дроплет для gitlab, но если зарегистрироваться и уже из кабинета начать создавать дроплет (точно так, как описано в tutorial выше), то такого ограничения нет.

2. Устанавливаем Jenkins.
Для этого случая, что не удивительно, у Digital Ocean Community тоже уже есть tutorial: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-jenkins-on-ubuntu-12-04. От версии ubuntu в этом руководстве вообще ничего не зависит, так что смело используйте и для 14ой убунту. Заходите на VPS по ssh и далее все как в tutorial.
По умолчанию Jenkins попытается использовать порт 8080, который уже занял gitlab. Нужно в файле /etc/default/jenkins заменить этот порт на какой-нибудь другой. Например, 9090.

Далее, наверняка захочется настроить для jenkins какое-нибудь приятное доменное имя, например ci.v-odnoklassnikov-moi-mir.ru. Для этого нам вполне хватит nginx, который установился вместе с gitlab. Его настройки лежат в каталоге /var/opt/gitlab/nginx/conf/.
Создадим файл /var/opt/gitlab/nginx/conf/jenkins.conf с содержимым из туториала jenkins: Jenkins behind an NGinX reverse proxy. Обратите внимание, что в нашем случае proxy_pass смотрит на "http://localhost:9090/;".
Только что написанный конфиг нужно "подцепить" в nginx GitLab'а. Данный процесс описан тут. Суть статьи по ссылке: нужно в файле /etc/gitlab/gitlab.rb, добавить строку:
nginx['custom_nginx_config'] = "include /var/opt/gitlab/nginx/conf/jenkins.conf;"
Не уверен, что файл  /var/opt/gitlab/nginx/conf/jenkins.conf не похерится во время обновления gitlab. Будем посмотреть...

Вот и все. В этом посте осознанно не останавливаюсь на настройках проектов в Jenkins, чтобы не ограничиваться целями конкретного проекта / платформы. Я использую дженкинс для авто-тестов python-проектов, но в нем есть все для тестирования мобильных приложений и еще много-много всякого.
Думаю, полезно было бы рассказать как при запросах на мердж jenkins умеет создавать отдельные проекты и тесты, как здорово использовать хуки gitlab'а, чтобы всегда иметь актуальную картину тестов, но все этом потом.

P. S. В комментах к посту помимо "<все не так>" и "<я тоже умный>" прошу написать: что еще по теме поста интересно лично тебе, мой дорогой подписчик?

P. P. S. Лучшей благодарностью будет зарегистрироваться в Digital Ocean по моей реферальной ссылке https://www.digitalocean.com/?refcode=e16041a7fe90. Вам не сложно, а мне приятно.

4 мая 2015 г.

Django-jenkins и COVERAGE_EXCLUDES_FOLDERS

Возился минут 10 пытаясь подобрать правильный способ прописать каталоги исключенные из coverage отчета для jenkins, залез в код и выяснил, что правильно определять полный путь до каталога, а не как в обсуждении на stackoverflow. Пора бы уже привыкнуть лезть в код раньше, чем на стэк =).
Вот кусок конфига для django + django-jenkins с покрытием:
import os


BASE_DIR = os.path.dirname(os.path.dirname(__file__))

# ...

PROJECT_APPS = ('myapp', )

JENKINS_TASKS = (
    'django_jenkins.tasks.run_pylint',
    'django_jenkins.tasks.run_pep8',
    'django_jenkins.tasks.run_pyflakes',
)

COVERAGE_EXCLUDES_FOLDERS = [
    os.path.join(BASE_DIR, 'myapp', 'tests')
]

Запускаются такие тесты так:
python manage.py jenkins --enable-coverage

14 апр. 2015 г.

Moscow Django MeetUp

Не мало воды утекло с тех пор, как я посещал последний раз это мероприятие. С удовольствием констатирую, что качество докладов сильно выросло: Moscow Django MeetUp № 26.

5 мар. 2015 г.

Интересный способ привлечь внимание пользователей bitbucket

Сегодня ночью мне пришло письмо от bitbucket, о том что не знакомый мне человек дает мне грант на запись в репозиторий. В названии репозитория доменное имя, по которому замещен интернет-магазин.
Грант, очевидно, был отозван сразу. Мне естественно, стало интересно посмотреть что там на сайте. Тему этого сайта для себя считаю закрытой, но какая-то более специализированная история, могла бы меня заинтересовать. Например, так можно раскручивать сервис непрерывной интеграции или аутсорс тестирования.
Для того что бы выдать грант пользователю достаточно знать его ФИ или username. Такие данные не сложно получить с помощью API bitbucket'а http://restbrowser.bitbucket.org/.

24 февр. 2015 г.

Timeout декоратор

Если функция будет работать долго, и при этом нужно чтобы она работала не дольше определенного количества секунд, можно применить такой вот декоратор:

import errno
import os
import signal
from functools import wraps


class TimeoutError(Exception):
    pass


def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
    """
    @timeout(60)
    def long_functions():
        <your code here>
    """
    def decorator(func):
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)

        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result

        return wraps(func)(wrapper)

    return decorator

Lock в файл или в redis?

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

Проще всего создать какой-нибудь lock-файл и проверять его существование. Если есть файл - lock включен.
import os
from contextlib import contextmanager


class LockedException(Exception):
    pass


@contextmanager
def file_lock(lock_file):
    """
    with file_lock('/tmp/file.lock'):
        some code here
    """
    if os.path.exists(lock_file):
        raise LockedException('LockedException')
    else:
        open(lock_file, 'w').write("1")
        try:
            yield
        finally:
            os.remove(lock_file)

Жалко напрягать HDD такими мелкими операциями. Попробуем делать lock в redis:
import redis
from contextlib import contextmanager


r = r = redis.StrictRedis(host='localhost', port=6379, db=0)


class LockedException(Exception):
    pass

@contextmanager
def redis_lock(lock_id):
    """
    with redis_lock('unique_redis_key'):
        some code here
    """
    if r.get(lock_id):
        raise LockedException('LockedException')
    else:
        r.set(lock_id, 1)
        try:
            yield
        finally:
            r.delete(lock_id)

Теперь проверим какой вариант быстрей:
if __name__ == '__main__':
    import time
    from numpy import mean


    count = 10000

    times1 = []
    for i in xrange(0, count):
        start = time.time()
        with file_lock('/tmp/file.lock'):
            pass

        times1.append(float(time.time() - start))

    times2 = []
    for i in xrange(0, count):
        start = time.time()
        with redis_lock('redis_lock'):
            pass

        times2.append(float(time.time() - start))

    print 'file lock:', max(times1), min(times1), mean(times1)
    print 'redis lock:', max(times2), min(times2), mean(times2)

Результаты:

maxminavg
file_lock0.08177495002750.000284910202026 0.000996773791313
redis_lock0.00140619277954 0.000319957733154 0.000346991157532

Lock в redis в 3 раза быстрей.

12 янв. 2015 г.

Shiningpanda и jenkins

Надкусил собаку, при настройке CI на базе jenkins. Проглотить мешают постоянные всякие мелочи. Например из последнего: плагин Virtualenv Builder [Shiningpanda], с помощью которого можно более лучше развертывать виртуальные окружения python, принимает настройку "name". По документации, настройка соответствует названию virtualenv окружения. Изменение этой опции не влияет на фактическое название окружения (по факту получается что-то вроде c934fdf).
При всем этом, Shiningpanda реально экономит время при создании CI для python проекта.

Сборка:
[Virtualenv Builder]
pip install pytest pytest-cov
$PYTHON_EXE setup.py install
py.test --junitxml junit.xml --cov-report xml --cov src tests.py

Постсборка:
[Cobertura coverage]
coverage.xml

[juint]
result.xml

И далее в постсборке запускает выкатка библиотеки и/или обновление девелоперского-стейджа.

А как в ваших компаниях реализован CI?

11 янв. 2015 г.

Отправка push-сообщений на мобильные устройства

Вчера меня в очередной раз спросили "как и чем лучше отправлять пуши?" Отвечаю: если у вас не очень много сообщений в месяц - идеально использовать http://urbanairship.com/. В нем есть много "богатой" аналитики прямо из коробки и он поможет вам акцентировать внимание на том, что действительно важно в этом вопрос: длинна сообщения, формат, скорость и гарантии доставки, отслеживания и аналитика. Есть бесплатный ознакомительный период, дающий максимум возможностей, но и бесплатной версии хватит, чтобы отправлять пуши на android, ios, win phone.
Если же количество отправляемых ежемесячно сообщений большое, а квартиру Москве продавать жалко - пишите свое решение, как поступили мы в Anews. В основе нашего решения django-push-notifications, который, в свою очередь, использовал код двух более низкоуровневых библиотек, которые постепенно были улучшены (без возврата кода в оные). Мы для себя улучшили поддержку юникода при отправке сообщений на iOS и добавили ttl в андройде (последние версии библиотеки ttl уже есть), обработку ответов о GCM сервера.
На данный момент на Anews мы почти ежедневно отправляем 500 тысяч сообщений, которые улетают к пользователям в среднем менее чем за 12 минут.

Блогинг на самописном джвике

В общем, эксперимент с блогом на самописном джвике можно считать завершенным. Больше всего этот опыт похож на попытку создать свою собственную почту вместо использования gmail - есть ощущение защищенности от Большого Брата, но нет тех печенек, которыми он заманивает на темную сторону.
Самая главная печенька, которой мне не хватало - отсев спам комментариев. Я натурально ловил себя на мысли, что не хочу заходить в админку блога, потому что там снова тонны скриптованных комментов с предложением сыграть в онлайн казино и сразу после этого медикаментозно увеличить себе пенис. Буэ.
После переноса постов из старого блога в новые прилетело сразу несколько комментов, которые я не получал к постам более года, потому как на том движке у меня не было старых подписчиков. Приятно получил комментарии к посту, но радость омрачает осознание того, что подписчики получили сразу пачку старых постов в RSS-ленте. Не удивительно, что общий тон комментариев раздраженно-осуждающий.

10 янв. 2015 г.

Интервью на openmonte.com

Очень интересный для меня опыт - интервью для сайта openmonte.com. Опубликовано почти месяц назад, но до этого было как-то не до блога. Паааадписывайтесь, ставьте лайк и др. и пр.! Что там еще нужно писать в таких случая? )

Идея!

Знающие люди утверждают, что сделать технологический бизнес сейчас не сложно. Куда сложней сделать масштабируемый бизнес. Скажем, сделать проект решающий проблему кросс-постинга в различные социальные сети и группы рекламного объявления не сложно. Как на этом заработать? Модель подписок можно считать масштабируемой?
Есть например http://post-it-quickly.com/ с именно такой моделью монетизации. Их посадочная страница уверяет, что дела идут не плохо. Какой потенциал может быть у подобной технологически короткой идеи?

Видео для "подумать":

Руслан - умница. Больше всего меня поразила его способность удерживать в голове прочитанные истории (ну и 70+ сотрудников, конечно). Для меня они (истории из книг) - подобие белого шума, руководствоваться ими мне тяжело. Ему - нет.