Привет, Хабр! Меня зовут Артем Желтак, я teamlead, а также преподаватель курса “Разработчик Golang” в OTUS. В преддверии старта нового потока курса, хочу поделиться с вами своей авторской статьей.
Я верю, что Golang прекрасен, но в мире еще много php и других проектов работающих на VPS, VDS. Можно поставить туда докер, но это (по мнению автора) переусложнение задачи. Можно компилировать файлик и загружать по FTP — небезопасно и не по феншую, SFTP — безопаснее, но снова не феншуй. Тогда давайте автоматизируем этот процесс через CircleCI. Мы будем по шагам писать файл конфигурации для CI, в конце соберем результат и запустим deploy.
На проекте с самого начала использовали приватный репозиторий bitbucket. (Сейчас приватные репозитории уже есть в гитхабе.) Не уходя из экосистемы Atlasian решили взять CircleCI (далее CI). Понравилось, что:
Создадим папку .circle и в ней создадим файл config.yml и опишем там ожидаемый workflow ( порядок исполнения задач)
Вот результат:
Мы описали паттерн, по которому каждый комит будет сначала проверяться тестами, далее выкатываться на dev, затем при ручном подтверждении отправляться на боевой сервер. Для наведения лоска добавим фильтр, что бы задача срабатывала только по тегу.
Начнем с запуска тестов, тут будет минимум кода.
После того как наш код протестирован и прошел code style проверки, можно делать deploy на dev. Я предлагаю использовать для запуска go сервиса supervisor (ver 3.1.4 на момент написания статьи), логи будем собирать им же.
Добавим в папку .circleci файлик supervisor_ph.conf, в рамках CI PH_NAME будет меняться на имя проекта. И в такой же файл будем писать вывод логов.
Всё, что отличает наш проект от других:
Для dev и prod изменяются только сервера и к названию приложения добавляется суффикс. Конфиг хранится в переменных окружения. (12 факторные приложения) Эту часть мы вынесем в environment, остальное продублируем.
Для уведомлений мы используем собственного бота, который вызывается через curl. Команда `when: on_fail` срабатывает если что-то пошло не так, ее можно также использовать для отката изменений. Хотя у нас это телеграмм бот, но в целом можно обойтись без него и использовать стандартные нотификации: Slack,IRC. Плюс уведомления об ошибках уходят на email.
Переменную `$TELEGRAM_SERVICE` добавим через раздел BUILD SETTINGS>Environment Variables.
Делаем push в github или в bitbucket. После идем в CircleCI в пункт Add project
Затем выбираем Start building. Финальным шагом будет добавления ssh ключа для авторизации на сервере под выбранным пользователем.
Всё можно делать deploy, ставим tag и начинаем радоваться жизни. Финальный вариант ./.circleci/config.yml — тут
Я верю, что Golang прекрасен, но в мире еще много php и других проектов работающих на VPS, VDS. Можно поставить туда докер, но это (по мнению автора) переусложнение задачи. Можно компилировать файлик и загружать по FTP — небезопасно и не по феншую, SFTP — безопаснее, но снова не феншуй. Тогда давайте автоматизируем этот процесс через CircleCI. Мы будем по шагам писать файл конфигурации для CI, в конце соберем результат и запустим deploy.
Требования к реализации
- Минимальные нововведения на сервере
- Deploy должен быть автоматизирован
- Входная точка для организации deploy — выставления тега для dev сборки и дополнительное ручное подтверждение для prod
- Сборка должна пройти автоматическое тестирование
- Механизм ручной откатки версии
Почему СircleCI?
На проекте с самого начала использовали приватный репозиторий bitbucket. (Сейчас приватные репозитории уже есть в гитхабе.) Не уходя из экосистемы Atlasian решили взять CircleCI (далее CI). Понравилось, что:
- минимальная настройка
- возможность debug по ssh
- бесплатная версия, но с ограничениями
- 2500 кредитов/в неделю (примерно 250 минут выполнения) #go собирается и деплоится быстро, нам хватит
- однопоточное исполнение #у нас не так много pet project
- только linux и windows #нам нужен linux
Часть первая, workflow
Создадим папку .circle и в ней создадим файл config.yml и опишем там ожидаемый workflow ( порядок исполнения задач)
workflows:
version: 2
tagged-build:
jobs:
- test
- dev_deploy:
requires:
- test
- approve_master_deploy:
type: approval
requires:
- test
- dev_deploy
- prod_deploy:
requires:
- dev_deploy
- approve_master_deploy
Вот результат:
Мы описали паттерн, по которому каждый комит будет сначала проверяться тестами, далее выкатываться на dev, затем при ручном подтверждении отправляться на боевой сервер. Для наведения лоска добавим фильтр, что бы задача срабатывала только по тегу.
- dev_deploy:
requires:
- test
filters:
branches:
ignore: /.*/
tags:
only: /.*/
Второй шаг, самый простой
Начнем с запуска тестов, тут будет минимум кода.
jobs:
test:
docker:
- image: circleci/golang:1.12
working_directory: ~/go-example/
steps:
- checkout
# тут будут linter'ы и тд
- run: go test -cover -v ./...
После того как наш код протестирован и прошел code style проверки, можно делать deploy на dev. Я предлагаю использовать для запуска go сервиса supervisor (ver 3.1.4 на момент написания статьи), логи будем собирать им же.
Добавим в папку .circleci файлик supervisor_ph.conf, в рамках CI PH_NAME будет меняться на имя проекта. И в такой же файл будем писать вывод логов.
[program:PH_NAME]
stopasgroup=true
user=deploy-user
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/PH_NAME.log
stderr_logfile=/var/log/supervisor/PH_NAME.log
redirect_stderr=true
Всё, что отличает наш проект от других:
Время Deploy
Для dev и prod изменяются только сервера и к названию приложения добавляется суффикс. Конфиг хранится в переменных окружения. (12 факторные приложения) Эту часть мы вынесем в environment, остальное продублируем.
prod_deploy:
environment:
TARGET_IP: 0.0.0.0
TARGET_DIR: /var/www/deploy-user/go-example
REMOTE_USER: deploy-user
SERVICE_NAME: go_example_prod
docker:
- image: circleci/golang:1.12
working_directory: ~/go-example/
steps:
- checkout
- add_ssh_keys #копируем добавленные в ci ключи, о них позже
- run: go build -ldflags "-X main.version=$CIRCLE_TAG" -o ./main ./src/main
- run: ssh -o "StrictHostKeyChecking=no" $REMOTE_USER@$TARGET_IP "mkdir $TARGET_DIR/v$CIRCLE_TAG" #создаем папку по номеру тега, где будут лежать исполняемые и вспомогательные файлы
- run: scp main $REMOTE_USER@$TARGET_IP:$TARGET_DIR/v$CIRCLE_TAG/ #загружаем исполняемый файл в директорию по тегу
- run: sed "s/PH_NAME/$SERVICE_NAME/g" .circleci/supervisor_ph.conf > .circleci/$SERVICE_NAME.conf
- run: echo command=$TARGET_DIR/v$CIRCLE_TAG/main >> .circleci/$SERVICE_NAME.conf
- run: scp .circleci/$SERVICE_NAME.conf $REMOTE_USER@$TARGET_IP:$TARGET_DIR/v$CIRCLE_TAG/
- run: ssh $REMOTE_USER@$TARGET_IP "ln -sf $TARGET_DIR/v$CIRCLE_TAG/$SERVICE_NAME.conf /etc/supervisord.d"
- run: ssh $REMOTE_USER@$TARGET_IP "supervisorctl -c /etc/supervisord.conf reread && supervisorctl -c /etc/supervisord.conf update"
- run: curl "$TELEGRAM_SERVICE?msg=$SERVICE_NAME%20v$CIRCLE_TAG%20deployed&channel=go_deploy"
Для уведомлений мы используем собственного бота, который вызывается через curl. Команда `when: on_fail` срабатывает если что-то пошло не так, ее можно также использовать для отката изменений. Хотя у нас это телеграмм бот, но в целом можно обойтись без него и использовать стандартные нотификации: Slack,IRC. Плюс уведомления об ошибках уходят на email.
Переменную `$TELEGRAM_SERVICE` добавим через раздел BUILD SETTINGS>Environment Variables.
- run:
command: curl "$TELEGRAM_SERVICE?msg=$SERVICE_NAME%20v$CIRCLE_TAG%20failed&channel=go_deploy"
when: on_fail
Финишная прямая
Делаем push в github или в bitbucket. После идем в CircleCI в пункт Add project
Затем выбираем Start building. Финальным шагом будет добавления ssh ключа для авторизации на сервере под выбранным пользователем.
Всё можно делать deploy, ставим tag и начинаем радоваться жизни. Финальный вариант ./.circleci/config.yml — тут
Alroniks
Я бы немного улучшил в целом процесс, добавив сборку артефактов и прикрепление оных к релизу, а уже после забирать их и деплоить на прод. Ну или заворачивать в докер и пушить в хаб, но это уже опционально.
jacksparrow Автор
Спасибо за комментарий, акцент в статье на VDS, к примеру, за 5$. На такую docker ставить излишне. Пока проект представляет из себя один бинарный файлик, можно его считать готовым артефактом, и загружать его в circleci или в свои репозитории. Тут этот шаг пропустил, т.к. сборка go быстрый процесс.