В начале разберёмся с текущими задачами.
Одна из частей нашей образовательной системы — курс в Google Classroom. В курсе публикуются задания для каждого урока. Так как выставление оценки студентам производится автоматически приложением, создавать задания необходимо тоже от имени приложения.
Для этого вам понадобится скрипт make_assignment.py
, который находится в папке classroom_utility
в приватном репозитории проекта. Возможно, вам понадобится установить библиотеки от google. После этого запустите файл и следуйте подсказкам на экране.
После этого в курсе появится новое задание. Обратите внимание на надпись с помощью API. Оценку за задание с такой надписью может быть поставлена приложением автоматически.
Теперь администратор может отредактировать или удалить созданное задание. Для этого нужно зайти в Classroom от имени сервисного аккаунта и внести изменения в созданное задание.
Обратите внимание на то, что имя задание жёстко закреплено: или название платформы (вернее, как это прописано в коде урока) или
"интерактивный урок по <название платформы>"
. Названия к регистру не чувствительны.
Список курсов находится в файле google_api\data\classrooms_list.py
В этом списке нужно перечислить псевдонимы курсов и их настоящие названия, как они указаны в Classroom.
Для работы приложения вам понадобится множество api-токенов, паролей и прочего. Для их хранения рекомендуется использовать файл с переменными окружения .env
(он находится в приватном репозитории и оттуда скачивается на сервер). Пример строчки из этого файла:
hook_url_ext=https://301.miem.vmnet.top/git/hook
Разберёмся, где это брать и как хранить.
В нашем проекте Google используется для многих целей: для авторизации пользователей (в будущем будет через smartpoint by hse), для доступа к гугл-таблице, для загрузки логов на гугл-диск и для модерирования классрума. Чтобы это обеспечить, нужен проект в Google Cloud Platform, в котором включены API диска, таблиц и классрума.
Сейчас мы используем два токена: один для авторизации, другой для всего остального. Первый токен типа Web application, а второй — Desktop. Первый предполагает, что пользователи видят некоторую web-страницу, с которым они могут взаимодействовать. Второй же предполагает, что мы видим экран нашего сервера. Как видите, последнее не очень правдоподобно, и в случае "наш сервер — сервер Google" взаимодействия нормальные люди используют так называемый сервисный аккаунт, но я не смог подключить этот сервисный аккаунт к классруму, поэтому пользуемся так, как получилось.
После того, как токены получены, нужно скопировать их данные в файл .env
.
.env
: credentials.json
.
Общение с пользователями осуществляется через чат-бота. Рассмотрим, как это делается в Zulip. Зайдите в chat.miem.hse.ru от имени сервисного аккаунта, перейдите в настройки → Ваши боты. Найдите (или создайте) бота и скопируйте API-ключ.
Укажите его в файле .env
в строчке zulip_api_key
. Теперь приложение сможет общаться с пользователями через этого бота.
Аналогично, все токены от платформ, по которым созданы уроки, также должны быть указаны в этом файле. Выберите подходящее имя и впишите токен.
Для использования Google недостаточно наличия токенов. Нужно вручную выполнить авторизацию от имени сервисного аккаунта. Это может быть необходимо в контейнерах web и log. Рассмотрим процесс авторизации:
sudo docker-compose exec <название контейнера, как в docker-compose> sh
. После этого вы попадаете внутрь конетйнера.run_auth.py
. В контейнере web, возможно, нужно выполнить команду export PYTHONPATH=/home/web/
. После этого запустите скрипт python3 run_auth.py
. После этого на экране появится ссылка. Скопируйте её, вставьте в адресную строку вашего браузера и авторизуйтесь от имени сервисного аккаунта.В контейнере web нужно перейти в папку /home/web/, выполнить команду
export PYTHONPATH=/home/web/
, затем запустить скриптpython3 core/google_api/run_auth.py
. (так сработало последний раз)
Приложение с точки зрения администратора состоит из пяти модулей: это web
(который рассматривается в статье Руководство для разработчиков новых уроков), receive
, log
, nginx
и certbot
. В будущем также будет модуль базы данных.
web | Основной модуль, обеспечивающий работу уроков, оценивание, отправку сообщений. |
receive | Модуль обеспечивает получение сообщений от пользователей, фильтрацию тех, которые являются допустимыми (stop и restart ) и отправку запросов в модуль web |
log | Этот модуль следит за логами, очищает их и выгружает на гугл-диск. [В будущем сможет это делать по запросу администратора в чат-бот.] |
api | Модуль помогает осуществлять api-запросы. |
nginx | Осуществляет проксирование всех запросов по адресу сайта на модуль web. Шифрует соединение сертификатом. |
certbot | Модуль нужен для получения новых сертификатов. Постоянно не запущен. |
Модули представляют собой Docker контейнеры, соединённые между собой. Изобразим структуру этих контейнеров:
Каждый контейнер — это python скрипт или программа, работающие на отдельной машине Linux. Сборка контейнеров происходит согласно файлам Dokerfile
в папке каждого модуля, а запуск всех контейнеров в нужной конфигурации происходит согласно файлу docker-compose.yml
. Подробнее о работе Docker читайте в документации.
Рассмотрим, как производится развёртывание на сервере.
git
, docker engine
и docker-compose
. Для их установки обратитесь к документации..env
в текущий каталог (файл должен оказаться рядом с файлом docker-compose.yml)sudo docker-compose up -d
. Произойдёт запуск сервисов в фоновом режиме. Чтобы проверить, какие контейнеры запущены, выполните sudo docker ps
. Чтобы увидеть вывод контейнеров, вместо первой команды используйте sudo docker-compose up
.Как перенести изменения из репозитория на сервер?
sudo docker ps -a
. Вы увидите список контейнеров. Скопируйте ID ненужного контейнера. После этого выполните команды: sudo docker stop <скопированный ID>
и sudo docker rm <скопированный ID>
. Если же вы хотите удалить все контейнеры, выполните команду sudo docker-compose down -v
sudo docker-compose build
sudo docker-compose up
(запускает все контейнеры) или sudo docker-compose up <имя контейнера, который вы хотите запустить>
. Имя возьмите из файла docker-compose.Теперь рассмотрим этот процесс более детально.
Рассмотрим Dockerfile
в папке web. Этот файл подготавливает образ Linux к тому, чтобы запустить на нём модуль web.
# web/Dockerfile
FROM registry.miem.hse.ru/301/onlineedu
COPY . /home/web
WORKDIR /home/web
RUN pip3 install -r requirements.txt
Первая строчка объявляет, что наш образ будет строиться поверх образа, который находится в хранилище проекта
на GitLab. Этот образ — Ubuntu с установленными средствами python и нужными для всех модулей зависимостями (про его сборку читайте ниже).
Вторая строчка копирует содержимое папки, в которой находится файл Dockerfile
(то есть папки web
), в каталог /home/web
внутри контейнера. Таким образом все файлы, которые находятся в папке web
, окажутся внутри контейнера, там мы их и будем запускать.
Треться строчка устанавливает этот каталог в качестве текущего
Четвёртая строчка проверяет и устанавливает недостающие библиотеки, согласно файлу requirements.txt
.
После выполнения докером этого файла мы получим образ с нашим модулем, готовый к запуску.
В папках log
и receive
находятся аналогичные Dockerfile
'ы для создания ещё двух аналогичных контейнеров, но с содержимым папок log и receive.
Список собранных и доступных образов можно увидеть, выполнив команду:
sudo docker image ls
.
Запуск всех нужных контейнеров с нужными параметрами осуществляется автоматически с помощью файла docker-compose.yml
. Вы найдёте его в папке репозитория.
Кратко разберём его содержимое
.
Видим, что в файле описываются наши пять сервисов.
Подробнее о томах читайте в описаниях к каждому модулю.
Следите, чтобы в файле
.env
не было кириллицы
Исходный образ собирается на основе образа Ubuntu. Dockerfile имеет следующий вид:
FROM ubuntu:latest
RUN apt-get update -y
RUN apt-get install -y python3 python3-pip python3-dev build-essential
COPY requirements.txt /maintmp/requirements.txt
WORKDIR /maintmp
RUN pip3 install -r requirements.txt
Для сборки и загрузки образа в регистр GitLab используются следующие команды:
sudo docker login registry.miem.hse.ru
sudo docker build -t registry.miem.hse.ru/301/onlineedu .
sydo docker push registry.miem.hse.ru/301/onlineedu
Ознакомиться с регистром можно на странице проекта
.
Посмотрим на часть файла docker-compose
, относящуюся к модулю web.
web:
env_file:
- .env
build: ./services/web
command: bash -c "export PYTHONPATH=/home/web/ && gunicorn --bind 0.0.0.0:5000 --log-level=debug --workers=4 --threads 2 wsgi:app"
ports:
- 5000:5000
volumes:
- ./services/web/core/google_api/:/home/web/core/google_api/
- ./services/web/core/data:/home/web/core/data
- ./services/web/core/logs:/home/web/core/logs
Для запуска здесь выполняются две команды. Первая команда устанавливает переменную окружения PYTHONPATH, чтобы можно было импортировать модули из соседних каталогов. Вторая команда запускает сервер gunicorn.
Перейдём к томам. Вторая строчка связывает папку core/data в контейнере с папкой на сервере, таким образом файл базы данных сохраняется на сервере, а не внутри контейнера. Третья строчка сохраняет файл с логами (core.txt
) на сервере, благодаря этому другие контейнеры (например, log
) также могут получить доступ к логам.
Первая строчка? С первой строчкой пока не понятно, как лучше сделать.
log:
env_file:
- .env
build: ./services/log
command: python3 sender.py
volumes:
- ./services/web/core/logs:/home/log/volume
stdin_open: true # docker run -i
tty: true # docker run -t
Здесь мы запускаем файл sender.py
, который отправляет логи на гугл-диск и очищает их. После этого программа приостанавливается на некоторое время.
При первом запуске может потребоваться авторизация приложения в гугл. Для этого предусмотрен файл run_auth.py
. Запустите терминал в этом контейнере (sudo docker-compose exec log sh
), найдите папку с файлом run_auth.py
и запустите его. Появится ссылка, которую нужно скопировать и открыть в браузере, после чего авторизоваться через сервисный аккаунт и скопировать код в терминал. После этого можно закрыть терминал, написав exit
.
В этом контейнере предусмотрен том для доступа к файлу с логами. Этот же файл использует контейнер web.
receive:
env_file:
- .env
build: ./services/receive
command: python3 receive.py
В этой части файла docker-compose ничего особенного, запускается файл receive.py
.
Рассмотрим этот файл
. Для интеграции с Zulip используется библиотека zulip
. Скрипт слушает сообщения, которые присылают боту. API-ключ бота сохранён в переменной окружения. При получении сообщения вызывается функция, которая анализирует сообщение, и если оно содержит stop или restart, то отправляется запрос в контейнер web. Это возможно, потому что все контейнеры объединены в одну сеть, и доступ к контейнеру можно получить по его названию.
api:
env_file:
- .env
build: ./services/api
volumes:
- ./services/web/core/google_api/data/:/home/api/volume/
- ./services/web/core/data:/home/api/data
- ./services/web/core/logs:/home/api/logs
command: python3 api.py
Здесь мы запускаем файл api.py
с тремя томами:
Сам модуль расположен в папке services/api. Он анализирует базу данных, и если в данный момент есть запущенный урок, то модуль с определённой периодичностью отправляет запрос к основному модулю, чтобы тот обратился к api сервиса.
Эти контейнеры используют сторонние, уже собранные образы. Но для них необходимо подготовить файл конфигурации.
Конфигурационный файл для сервера nging находится в папке /nginx_data/nginx. В этом файле должно быть прописано проксирование запросов с 443 порта на web контейнер, с 80 порта на 443 порт, а также на файлы, необходимые для шифрования соединения и получения сертификата Let's Encrypt. Подробнее об этом написано в этой инструкции.
Важно, чтобы в файле конфигурации был прописан настоящий адрес вашего сервера.
Для автоматизации процесса выдачи сертификатов предлагается использовать скрипт init-letsencrypt.sh
. В нём нужно исправить пути и адрес сайта на настоящие, а дальше запустить с правами суперпользователя. После этого в указанной папке появятся заново полученные сертификаты и ключи, а также уже будет запущен nginx.
Важно: для запуска контейнера nginx должен быть запущен контейнер web. Запустите его перед этим вручную.
Обратите внимания на тома у контейнеров nginx и certbot: они позволяют nginx'у получить доступ к сертификатам, сгенерированным certbot'ом.
Если сертификаты уже получены и находятся в папке nginx_data/certbot/conf, то запускать скрипт не надо, достаточно пользоваться командой docker-compose up
.
Для начала — можно ознакомиться с логами. Последние логи доступны по ссылке https://301.miem.vmnet.top/log
, а логи за более давние периоды — на гугл-диске. В логах документируются возникающие ошибки.
Пока что мы пользуемся базой SQLite. Скачать файл с текущими записями можно по ссылке https://301.miem.vmnet.top/bdfile
. Обратите внимание, что браузер часто может не скачивать файл с сервера, а доставать его из кэша. В таком случае воспользуйтесь утилитой curl. Для просмотра файла подойдёт, например, программа DB Browser (SQLite).