Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 5 additions & 146 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,147 +1,6 @@
# Шаблон сервиса рекомендаций
# Интерпретируемость в рекомендательных системах
Так как можно было придумать свое, я реализовал интерпретируемость через схожесть жанров рекомендаций и исторических данных. Алгоритм работает для всех пользователей.
1. Если пользователь холодный, то ему присылаются популярные рекомендации
2. Если пользователь горячий, то выводим схожесть в процентном соотношении

## Подготовка

### Python

В данном шаблоне используется Python3.8, однако вы можете использовать более свежие версии, если хотите.
Но мы не гарантируем, что все будет работать.

### Make

[Make](https://www.gnu.org/software/make/) - это очень популярная утилита,
предназначенная для преобразования одних файлов в другие через определенную последовательность команд.
Однако ее можно использовать для исполнения произвольных последовательностей команд.
Команды и правила их исполнения прописываются в `Makefile`.

Мы будем активно использовать `make` в данном проекте, поэтому рекомендуем познакомится с ней поближе.

На MacOS и *nix системах `make` обычно идет в комплекте или ее можно легко установить.
Некоторые варианты, как можно поставить `make` на `Windows`,
описаны [здесь](https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows).

### Poetry

[Poetry](https://python-poetry.org/) - это удобный инструмент для работы с зависимостями в Python.
Мы будем использовать его для подготовки окружения.

Поэтому перед началом работы необходимо выполнить [шаги по установке](https://python-poetry.org/docs/#installation).


## Виртуальное окружение

Мы будем работать в виртуальном окружении, которое создадим специально для данного проекта.
Если вы не знакомы с концепцией виртуальных окружений в Python, обязательно
[познакомьтесь](https://docs.python.org/3.8/tutorial/venv.html).
Мы рекомендуем использовать отдельное виртуальное окружение для каждого вашего проекта.

### Инициализация окружения

Выполните команду
```
make setup
```

Будет создано новое виртуальное окружение в папке `.venv`.
В него будут установлены пакеты, перечисленные в файле `pyproject.toml`.

Обратите внимание: если вы один раз выполнили `make setup`, при попытке повторного ее выполнения ничего не произойдет,
поскольку единственная ее зависимость - директория `.venv` - уже существует.
Если вам по какой-то причине нужно пересобрать окружение с нуля,
выполните сначала команду `make clean` - она удалит старое окружение.

### Установка/удаление пакетов

Для установки новых пакетов используйте команду `poetry add`, для удаления - `poetry remove`.
Мы не рекомендуем вручную редактировать секцию с зависимостями в `pyproject.toml`.

## Линтеры, тесты и автоформатирование

### Автоформатирование

Командой `make format` можно запустить автоматическое форматирование вашего кода.

Сейчас ее выполнение приведет лишь к запуску [isort](https://github.com/PyCQA/isort) - утилиты
для сортировки импортов в нужном порядке.
При желании вы также можете добавить другие инструменты, например [black](https://github.com/psf/black) или
[yapf](https://github.com/google/yapf), которые могут действительно отформатировать код.


### Статическая проверка кода

Командой `make lint` вы запустите проверку линтерами - инструментами для статического анализа кода.
Они помогают выявить ошибки в коде еще до его запуска, а также обнаруживают несоответствия стандарту
[PEP8](https://peps.python.org/pep-0008).

### Тесты

Командой `make test` вы запустите тесты при помощи утилиты [pytest](https://pytest.org/).


## Запуск приложения

### Способ 1: Python + Uvicorn

```
python main.py
```

Приложение запустится локально, в одном процессе.
Хост и порт по умолчанию: `127.0.0.1` и `8080`.
Их можно изменить через переменные окружения `HOST` и `PORT`.

Управляет процессом легковесный [ASGI](https://asgi.readthedocs.io/en/latest/) server [uvicorn](https://www.uvicorn.org/).

Обратите внимание: для запуска нужно использовать `python` из окружения проекта.

### Способ 2: Uvicorn

```
uvicorn main:app
```

Очень похож на предыдущий, только запуск идет напрямую.
Хост и порт можно передать через аргументы командной строки.

Обратите внимание: для запуска нужно использовать `uvicorn` из окружения проекта.


### Способ 3: Gunicorn

```
gunicorn main:app -c gunicorn.config.py
```

Способ похож на предыдущий, только вместо `uvicorn` используется
более функциональный сервер [gunicorn](https://gunicorn.org/) (`uvicorn` используется внутри него).
Параметры задаются через конфиг, хост и порт можно задать
через переменные окружения или аргументы командной строки.

Сервис запускается в несколько параллельных процессов, по умолчанию их число
равно числу ядер процессора.

Обратите внимание: для запуска нужно использовать `gunicorn` из окружения проекта.

### Способ 4: Docker

Делаем все то же самое, но внутри docker-контейнера.
Если вы не знакомы с [docker](https://www.docker.com/), обязательно познакомьтесь.

Внутри контейнера можно использовать любой из способов, описанных выше.
В продакшене рекомендуется использовать `gunicorn`.

Собрать и запустить образ можно командой

```
make run
```

## CI/CD

Когда вы делаете пуш в гит (в любую ветку), выполняется процесс CI.
Что именно выполняется в этом процессе и как он триггерится (в данном случае по пушу),
описывается в специальных `.yaml` конфигах в папке `.github/workflows`.

Сейчас там есть только один конфиг, который запускает процесс, в котором создается виртуальное окружение,
прогоняются линтеры и тесты. Если что-то пошло не так, процесс падает с ошибкой и в Github появляется красный крестик.
Вам нужно посмотреть логи, исправить ошибку и запушить изменения.
- Отсутствуют юнит тесты
4 changes: 1 addition & 3 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@


if __name__ == "__main__":

host = os.getenv("HOST", "127.0.0.1")
port = int(os.getenv("PORT", "8080"))

port = int(os.getenv("PORT", "8000"))
uvicorn.run(app, host=host, port=port)
97 changes: 97 additions & 0 deletions mlflow/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
version: '3.7'

services:
minio:
restart: always
image: minio/minio:RELEASE.2022-04-09T15-09-52Z
container_name: mlflow_s3
ports:
- "9000:9000"
- "9001:9001"
command: server /data --console-address ':9001' --address ':9000'
environment:
- MINIO_ROOT_USER=${AWS_ACCESS_KEY_ID}
- MINIO_ROOT_PASSWORD=${AWS_SECRET_ACCESS_KEY}
volumes:
- minio_data:/data

mc:
image: minio/mc:RELEASE.2022-04-07T21-43-27Z
depends_on:
- minio
container_name: mc
entrypoint: >
/bin/sh -c "
/tmp/wait-for-it.sh minio:9000 &&
/usr/bin/mc alias set minio http://minio:9000 ${AWS_ACCESS_KEY_ID} ${AWS_SECRET_ACCESS_KEY} &&
/usr/bin/mc mb minio/mlflow;
exit 0;
"
volumes:
- ./wait-for-it.sh:/tmp/wait-for-it.sh

db:
restart: always
# image: mysql/mysql-server:8.0.28-1.2.7-server
image: postgres:15.1-alpine3.16
container_name: mlflow_db
ports:
- "${DB_PORT}:${DB_PORT}"
environment:
# - MYSQL_DATABASE=${DB_NAME}
# - MYSQL_USER=${DB_USER}
# - MYSQL_PASSWORD=${DB_PASS}
# - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
volumes:
# - dbdata:/var/lib/mysql
- dbdata:/var/lib/postgresql/data

web:
restart: always
build: ./mlflow
image: mlflow_server
container_name: mlflow_server
depends_on:
- mc
- db
ports:
- "${MLFLOW_PORT}:${MLFLOW_PORT}"
environment:
- MLFLOW_S3_ENDPOINT_URL=http://minio:9000
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
command: >
mlflow server
--backend-store-uri ${DB_TYPE}://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}
--default-artifact-root s3://mlflow/
--host 0.0.0.0
--port ${MLFLOW_PORT}

nlp:
build: ./examples
image: mlflow_nlp_demo
depends_on:
- web
container_name: mlflow_client
environment:
- MLFLOW_TRACKING_URI=http://web:${MLFLOW_PORT}
- MLFLOW_S3_ENDPOINT_URL=http://minio:9000
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
command: >
/bin/sh -c "
/tmp/wait-for-it.sh web:${MLFLOW_PORT} -t 30 &&
cd /work/examples && python main.py;
exit 0;
"
volumes:
- ./wait-for-it.sh:/tmp/wait-for-it.sh
- ./:/work


volumes:
dbdata:
minio_data:
Binary file added mlflow/docker-localhost.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading