Date

Использование Slack в делой пайплайне реально прикольное, и приносит много-много бенефитов.

Почему?

Отходя в сторону от фана, основной бенефит от ChatOps - это последняя точка абсолютного доверия к деплою. Если вы юзаете бест пректис в разработке, Ваш код однозначно версионирован (у каждого релиза есть своя версия), схема базы данных тоже версионирована, и, конечно инфраструктура версионирована. А как же код, который деплоит? (скрипты, fabric, ansible, salt, puppet, chef ...) Естественно, это тоже должно быть версионировано. Но, стоит заметить, что код для деплоя запускается непонятно где, и никто не может дать гарантию, что он не изменен, что это именно нужная последняя версия, что в папочке с кодом все чисто и правильно. Никто не может дать гарантию. Мы можем быть уверенны в этом только в том случае, если у нас есть централизованное место, из которого все, кто хочет доставить новую версию аппа, запускают деплой скрипты. Всем понятно, что это не может быть сделано по SSH - появляется человеческий фактор. И тут на арену выходит бот. Slack bot.

Второй основной концепцией поставки в ChatOps является коммуникация. Каждый член команды разработки хочет знать, когда начался деплой, когда он закончился, и успешно ли он прошел. Каждый хочет видеть историю деплоев. Интересно знать что именно сейчас находится на инвайроменте. Возможно кто-то делал роллбек? Если использовать Slack bot, мы получаем эти бенефиты из коробки.

И еще один очень крутой бенефит. Вам не нужен именно Ваш ноутбук для деплоя. Вы можете перезапустить докер контейнер, или сделать роллбек прямо из телефона! Все что нужно - это скачать себе Slack клиент. В свою очередь, такой подход дает возможность правильно менеджить доступы (у кого есть права деплоить, у кого нету). Просто пригласите нужных людей в нужный канал!

Почему именно бот, а не просто команда со слешем?

У Slack есть очень крутые команды, которые начинаются со /, и их можно использовать для того, чтобы затриггерить что-либо, используя веб хук, и вернуть результаты в Slack. Действительно, мы можем использовать эту фичу для деплоя, но бот - это решение намного лучше, и на это есть 2 причины.

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

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

Как выглядит деплой в Slack?

Это скриншот Slack во время нашего актуального деплоя. Нашего бота зовут Nestor и я задеплоил новую версию фронтенда на наши сервера в AWS.

Slack Deploy

Скелетон кода для бота

Это наш скелетон для бота в Slack, мы используем его в https://www.serenytics.com/. Это весь скрипт, который Вы можете дописать (заполнив пустые поля), и запустить где-либо. Все будет работать. Просто вставьте Ваш токен и код для деплоя. Если вы хотите прочитать больше про ботов для Slack, https://api.slack.com/bot-users.

import logging
import os
import time
import traceback


# pip install slackclient
from slackclient import SlackClient


SLACK_BOT_USER = 'YOUR_SLACK_BOT_USER_ID'
SLACK_BOT_MENTION = '<@%s>' % SLACK_BOT_USER
SLACK_BOT_NAME = 'nestor'
SLACK_CHANNEL = 'ask-nestor'

SLACK_TOKEN = os.environ.get('SLACK_TOKEN')

slack_client = SlackClient(SLACK_TOKEN)


class HelpException(Exception):
    pass


def send_message(text):
    slack_client.rtm_send_message(channel=SLACK_CHANNEL, message=text)


def has_conversation_started_with(user):
    pass


def process_conversation(cmd, event):
    pass


def process_help(*args):
    pass


def process_deploy(cmd, event):
    pass


def process_rollback(cmd, event):
    pass


def process_restart(cmd, event):
    pass


def process_event(event):
    # filter out slack events that are not for us
    text = event.get('text')
    if text is None or not text.startswith((SLACK_BOT_NAME, SLACK_BOT_MENTION)):
        return

    # make sure our bot is only called for a specified channel
    channel = event.get('channel')
    if channel is None:
        return
    if channel != sc.server.channels.find(SLACK_CHANNEL).id:
        send_message('<@{user}> I only run tasks asked from `{channel}` channel'.format(user=event['user'],
                                                                                        channel=SLACK_CHANNEL))
        return

    # remove bot name and extract command
    if text.startswith(SLACK_BOT_MENTION):
        cmd = text.split('%s' % SLACK_BOT_MENTION)[1]
        if cmd.startswith(':'):
            cmd = cmd[2:]
        cmd = cmd.strip()
    else:
        cmd = text.split('%s ' % SLACK_BOT_NAME)[1]

    # process command
    try:
        if has_conversation_started_with(event['user']):
            process_conversation(cmd, event)
        elif cmd.startswith('help'):
            process_help(cmd, event)
        elif cmd.startswith('deploy '):
            process_deploy(cmd, event)
        elif cmd.startswith('rollback '):
            process_rollback(cmd, event)
        elif cmd.startswith('restart '):
            process_restart(cmd, event)
        else:
            send_message("*I don't know how to do that*: `%s`" % cmd)
            process_help()
    except HelpException:
        return process_help()


def process_events(events):
    for event in events:
        try:
            process_event(event)
        except Exception as e:
            logging.exception(e)
            msg = '%s: %s\n%s' % (e.__class__.__name__, e, traceback.format_exc())
            send_message(msg)


def main():
    if slack_client.rtm_connect():
        send_message('_starting..._')

        # --
        # Here is a good place to init git repositories if needed, in order to provide git-based features:
        # - list of commits to deploy
        # - history of deployments
        # - status of deployed services vs what's available in git

        send_message("*All right, I'm ready, ask me anything!*")

        while True:
            events = slack_client.rtm_read()
            if events:
                logging.info(events)
            process_events(events)
            time.sleep(0.1)
    else:
        logging.error('Connection Failed, invalid token?')


if __name__ == '__main__':
    main()

Это - перевод

Статья и правда мне очень понравилась, в ней представлен достаточно показательный пример ChatOps, описаны бенефиты и недостатки. Я не совсем согласен с примером на скриншоте, скорее всего бот должен именно стучать в централизированное API, а не реализовывать внутри себя все степы деплоя, но это - нюансы имплементации. Каждый делает, как хочет и как знает. Я уверен, что возможно доказать целесообразность любого решения, поэтому спорить не буду. Оригинал можете прочитать тут: https://tech-blog.serenytics.com/deploy-your-saas-with-a-slack-bot-f6d1fc764658#.d6yack518


Comments

comments powered by Disqus