Настоящий workflow
Измените код файла main.yaml: теперь workflow будет проверять код проекта на соответствие PEP8 и запускать тесты.
main.yaml
Скопировать кодYAML
name: Flask-app workflow
on: [push]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install -r requirements.txt
- name: Lint with flake8
run: flake8 .
- name: Test with pytest
run: pytest tests.py
В коде workflow для клонирования репозитория применён предустановленный action checkout, а для установки Python-окружения — action setup-python. Команды для выполнения этих операций можно прописать вручную прямо в workflow, но проще применить готовые скрипты.
Детальнее познакомиться с этими actions можно здесь:
Для последовательного запуска команд применяется такой синтаксис:
Скопировать кодYAML
run: |
команда1
команда2
команда3
В шаге Install dependencies (англ. «установка зависимостей») сначала обновляется pip, затем устанавливаются необходимые пакеты для тестов. После этого идёт установка всех зависимостей из requirements.txt.
Тесты для flask-проекта
В головной директории проекта создайте файл tests.py и добавьте туда пару тестов, чтобы проверить, как всё работает.
Первый тест проверяет, что при GET-запросе на корневой URL возвращается строка «У меня получилось!».
Второй тест проверяет, что при POST-запросе к главной странице проекта возвращается ошибка «код 405, метод не разрешен».
Скопировать кодPYTHON
import unittest
import hello as tested_app
class FlaskAppTests(unittest.TestCase):
def setUp(self):
tested_app.app.config['TESTING'] = True
self.app = tested_app.app.test_client()
def test_get(self):
r = self.app.get('/')
self.assertEqual(r.data.decode('utf-8'), 'У меня получилось!')
def test_post(self):
r = self.app.post('/')
self.assertEqual(r.status_code, 405)
if __name__ == '__main__':
unittest.main()
Отлично! Теперь создайте Dockerfile в корневой директории проекта.
Скопировать кодPYTHON
FROM python:3.8-alpine
COPY ./ /app
RUN pip install -r /app/requirements.txt
EXPOSE 5000
CMD python /app/hello.py
Теперь сбилдите образ командой
docker build ., затем запустите и зайдите в браузере на
http://0.0.0.0:5000Скопировать кодPYTHON
docker run -p 5000:5000 <CONTAINER ID>
Теперь можно запушить все файлы в репозиторий и убедиться, что ошибок нет.
Автоматическая сборка и пуш docker-образа
Если тесты прошли успешно и код обновлён — нужно обновить docker-образ с проектом на DockerHub.
По умолчанию все jobs запускаются одновременно. Но чаще требуется последовательный запуск: например, задача tests должна быть запущена только после успешного выполнения задачи build, а deploy — только по окончании tests.
Для определения последовательности запуска есть ключ needs, в нём указывается имя того job, после выполнения которого должна запуститься описываемая задача.
Скопировать кодYAML
name: CI
on:
push:
branches: [ main ]
...
jobs:
tests:
runs-on: ubuntu-latest
steps:
- ...
build:
runs-on: ubuntu-latest
needs: tests # не запускать сразу, ждать, пока выполнится tests
steps:
- ...
deploy:
runs-on: ubuntu-latest
needs: build # не запускать сразу, ждать, пока выполнится build
steps:
- ...
Добавьте в workflow новый job для автоматической пересборки и обновления образа на DockerHub. Он должен выполниться только после успешного прохождения тестов.
Скопировать кодYAML
build_and_push_to_docker_hub:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
needs: tests
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push to Docker Hub
uses: docker/build-push-action@v2
with:
push: true
tags: <логин-на-docker-hub>/<репозиторий-на-docker-hub>:latest
Приватные данные, переменные DOCKER_USERNAME и DOCKER_PASSWORD, нужно сохранить в надёжном месте. Для этого в GitHub Actions есть система Secrets.
Как хранить секреты в GitHub Actions
На платформе GitHub Actions можно хранить секреты (переменные окружения, которые содержат приватные данные) в зашифрованном виде прямо в репозитории, в системе Secrets, это очень удобно.
Создавать Secrets можно только в собственных репозиториях.
- Перейдите в настройки репозитория Settings, выберите на панели слева Secrets, нажмите New secret:
- Задайте имя секрета и его значение и нажмите Add secret. Сохраните переменные
DOCKER_USERNAME и DOCKER_PASSWORD с необходимыми значениями.
Секрет появится в списке:
Обращаться к нему можно так: ${{ secrets.<имя-токена> }}
Правила именования Secrets:
- Названия могут содержать цифры и символы английского алфавита в любом регистре, а также символ нижнего подчеркивания. Рекомендуется использовать символы верхнего регистра, как для констант.
- Названия не могут начинаться с цифры и с префикса
GITHUB_
Deploy
Ваш проект проверяется тестами, образ автоматически обновляется в Docker Hub, теперь можно настроить автоматический деплой проекта: проект должен деплоиться, когда в Git происходит пуш в ветку main.
Чтобы обновить код на боевом сервере, нужно подтянуть туда новый Docker-образ с сервера GitHub Actions, выполнив на сервере команду docker pull, остановить старый контейнер и запустить новый.
Этот процесс можно автоматизировать с помощью GitHub Actions, применив специальный action для выполнения ssh-команд.
Ваш боевой сервер на Яндекс.Облаке должен получить данные с «незнакомого» временного сервера GitHub Actions. Для безопасного соединения нужно добавить на сервер GitHub Actions ssh-ключ с боевого сервера.
Сохраните ключ с боевого сервера в Secrets на GitHub Actions:
Скопируйте приватный ключ со своего сервера в Яндекс.Облаке:
Скопировать кодPYTHON
cat ~/.ssh/id_rsa
- На GitHub Actions сохраните ключ в Secrets в переменную
SSH_KEY. - На GitHub Actions сохраните ещё две переменные:
- в переменной
USER сохраните имя пользователя для подключения к серверу - в переменной
HOST сохраните IP-адрес вашего сервера. Если при создании ssh-ключа вы использовали фразу-пароль, то добавьте также переменную PASSPHRASE.
Добавьте ещё один job в workflow:
Скопировать кодYAML
deploy:
runs-on: ubuntu-latest
needs: build_and_push_to_docker_hub
steps:
- name: executing remote ssh commands to deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
key: ${{ secrets.SSH_KEY }}
passphrase: ${{ secrets.PASSPHRASE }} # если ваш ssh-ключ защищен фразой-паролем
source: "docker-compose.yaml, nginx/default.conf"
target: "~/app/"
script: |
sudo docker pull <имя-пользователя>/<имя-репозитория>
sudo docker stop $(sudo docker ps -a -q)
sudo docker run --rm -d -p 5000:5000 <имя-пользователя>/<имя-репозитория>
Готово!
Запустите workflow и убедитесь, что всё работает.
После этого попробуйте изменить что-то в коде, например добавьте ещё два восклицательных знака (не забудьте также добавить их в тесты!), затем выполните git push — и через пару минут код обновится на сервере.
Вы можете открыть в репозитории вкладку Actions и наблюдать за выполнением всех стадий.
Отправка отчета
Остался финальный шаг. Неудобно каждый раз отслеживать процесс на GitHub и ждать завершения всех шагов, чтобы посмотреть результат. Можно попросить бота отправить оповещение об успешном выполнении всех шагов workflow.
А у вас как раз уже есть готовый бот! Хватит ему бездельничать.
Добавьте четвёртый шаг в workflow:
Скопировать кодYAML
send_message:
runs-on: ubuntu-latest
needs: deploy
steps:
- name: send message
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
message: ${{ github.workflow }} успешно выполнен!
Зайдите в Settings → Secrets в вашем репозитории и установите секретные переменные:
- чтобы бот отправил сообщение именно вам — в переменную
TELEGRAM_TO добавьте ID своего телеграм-аккаунта. Узнать свой ID можно у бота @userinfobot. - в переменную
TELEGRAM_TOKEN добавьте токен вашего бота. Получить этот токен можно у бота @BotFather.
Если вы хотите отправлять сообщение в Slack или куда-то ещё — найдите нужный action в маркетплейсе GitHub Actions и подключите его по инструкции.
Вот теперь точно всё! Ваш workflow состоит из четырёх шагов:
- Тестирование проекта
- Сборка и публикация образа
- Автоматический деплой
- Отправка уведомления в персональный чат
Ну разве не чудесно?
Настройка workflow потребовала от вас времени и усилий, однако в процессе работы над проектом от автоматизации процессов вы получите многократный выигрыш.