- PVSM.RU - https://www.pvsm.ru -
В этой статье я хотел бы рассказать о своем хобби-проекте поиска и классификации объявлений о сдаче квартир из социальной сети ВКонтакте и опыте его переезда на k8s.
В марте 2017 года я запустил сервис по парсингу и классификации объявлений о сдаче квартир из социальной сети ВКонтакте.
Вот тут [1] можно подробнее прочитать о том, как я пытался классифицировать объявления разными способами и в итоге остановился на лексическом парсере Yandex Tomita Parser [2].
Вот тут [3] можно почитать об архитектуре проекта на старте его существования и о том, какие технологии при этом использовались и почему.
Разработка первой версии сервиса заняла примерно год.
Для разворачивания каждого компонента сервиса я написал скрипты на Ansible [4].
Периодически сервис не работал из-за ошибок в переусложнённом коде или неверной настройки компонентов.
Примерно в июне 2019 года в коде парсера обнаружилась ошибка, из-за которой не собирались новые объявления. Вместо очередного исправления было принято решение временно его отключить.
Поводом для восстановления сервиса стало изучение k8s.
k8s [5] — открытое программное обеспечение для автоматизации развёртывания, масштабирования контейнеризированных приложений и управления ими.
Вся инфраструктура сервиса описывается конфигурационными файлами в формате yaml (чаще всего).
Я не буду рассказывать о внутреннем устройстве k8s, а только дам немного информации о некоторых его компонентах.
containers:
- name: collect-consumer
image: mrsuh/rent-collector:1.3.1
envFrom:
- configMapRef:
name: collector-configmap-1.1.0
- secretRef:
name: collector-secrets-1.0.0
apiVersion: apps/v1
kind: Deployment # тип Deployment
metadata:
name: deployment-name # имя Deployment
labels:
app: deployment-label-app # Label Deployment
spec:
selector:
matchLabels:
app: pod-label-app # Label, по которому Deployment понимает за какими Pods нужно следить
template:
metadata:
name: pod-name
labels:
app: pod-label-app # Label Pod
spec:
containers:
- name: container-name
image: mrsuh/rent-parser:1.0.0
ports:
- containerPort: 9080
---
apiVersion: v1
kind: Service # тип Service
metadata:
name: service-name # имя Service
labels:
app: service-label-app # Label Service
spec:
selector:
# Тип Service не подерживает matchLabels, как Deployment, но фильтрует все равно по Labels
app: pod-label-app # Label, по которому Service понимает, на какой Pod нужно отправлять трафик
ports:
- protocol: TCP
port: 9080
type: NodePort
Чтобы сервис стал вести себя более стабильно и предсказуемо, пришлось убрать все дополнительные компоненты, которые плохо работали, и немного переписать основные.
Так, я принял решение отказаться от:
После всех изменений сервис изнутри стал выглядеть вот так:
Для того, чтобы управлять компонентами и мониторить их в едином стиле, я решил:
В самих образах нет ничего специфичного.
Таким образом, у меня появились компоненты в образах Docker, и я приступил к разработке конфигурации k8s.
Все компоненты, которые работают как демоны, я выделил в Deployment.
Каждый демон должен быть доступен внутри кластера, поэтому у всех есть Service.
Все таски, которые должны исполняться периодически, работают в CronJob.
Вся статика (картинки, js, css) хранится в контейнере view, а раздавать её должен контейнер Nginx.
Оба контейнера находятся в одном Pod.
Файловая система в Pod не шарится, но можно при старте Pod скопировать всю статику в общую для обоих контейнеров папку emptyDir.
Такая папка будет шариться для разных контейнеров, но только внутри одного Pod.
apiVersion: apps/v1
kind: Deployment
metadata:
name: view
spec:
selector:
matchLabels:
app: view
replicas: 1
template:
metadata:
labels:
app: view
spec:
volumes:
- name: view-static
emptyDir: {}
containers:
- name: nginx
image: mrsuh/rent-nginx:1.0.0
- name: view
image: mrsuh/rent-view:1.1.0
volumeMounts:
- name: view-static
mountPath: /var/www/html
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "cp -r /app/web/. /var/www/html"]
Компонент collector используется в Deployment и CronJob.
Все эти компоненты обращаются к API ВКонтакте и должны где-то хранить общий токен доступа.
Для этого я использовал PersistentVolumeClaim, который подключил к каждому Pod.
Такая папка будет шариться для разных Pod, но только внутри одной ноды.
apiVersion: apps/v1
kind: Deployment
metadata:
name: collector
spec:
selector:
matchLabels:
app: collector
replicas: 1
template:
metadata:
labels:
app: collector
spec:
volumes:
- name: collector-persistent-storage
persistentVolumeClaim:
claimName: collector-pv-claim
containers:
- name: collect-consumer
image: mrsuh/rent-collector:1.3.1
volumeMounts:
- name: collector-persistent-storage
mountPath: /tokenStorage
command: ["php"]
args: ["bin/console", "app:consume", "--channel=collect"]
- name: parse-consumer
image: mrsuh/rent-collector:1.3.1
volumeMounts:
- name: collector-persistent-storage
mountPath: /tokenStorage
command: ["php"]
args: ["bin/console", "app:consume", "--channel=parse"]
Для хранения данных БД также используется PersistentVolumeClaim.
В итоге получилась вот такая схема (в блоках собраны Pods одного компонента):
Для начала я развернул кластер локально с помощью Minikube [7].
Конечно, не обошлось без ошибок, поэтому мне очень помогли команды
kubectl logs -f pod-name
kubectl describe pod pod-name
После того, как я научился разворачивать кластер в Minikube, для меня не составило труда развернуть его в DigitalOcean.
В заключение могу сказать, что сервис стабильно работает уже 2 месяца.
Полную конфигурацию можно посмотреть тут https://github.com/mrsuh/rent-k8s [8].
Автор: Anton Sukhachev
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/343982
Ссылки в тексте:
[1] тут: https://habr.com/en/post/342220
[2] Yandex Tomita Parser: https://yandex.ru/dev/tomita/
[3] тут: https://habr.com/en/post/328282
[4] Ansible: https://ru.wikipedia.org/wiki/Ansible
[5] k8s: https://ru.wikipedia.org/wiki/Kubernetes
[6] Ceph: https://ru.wikipedia.org/wiki/Ceph
[7] Minikube: https://habr.com/en/company/flant/blog/333470/
[8] https://github.com/mrsuh/rent-k8s: https://github.com/mrsuh/rent-k8s
[9] Источник: https://habr.com/ru/post/484528/?utm_source=habrahabr&utm_medium=rss&utm_campaign=484528
Нажмите здесь для печати.