Comment j'ai transféré mon projet de passe-temps sur K8s

image

Dans cet article, je voudrais parler de mon projet de recherche et de classification d'annonces pour la location d'appartements à partir du réseau social VKontakte et de l'expérience de son passage à k8s.


Table des matières


  • Un peu sur le projet
  • Présentation des K8
  • Se préparer au déménagement
  • Développement de configuration K8s
  • Déploiement du cluster K8s

Un peu sur le projet


img



En mars 2017, j'ai lancé un service d'analyse et de classification des annonces de location d'appartements à partir du réseau social VKontakte.


Ici, vous pouvez en savoir plus sur la façon dont j'ai essayé de classer les annonces de différentes manières et, finalement, sur l'analyseur lexical Yandex Tomita Parser .


Ici, vous pouvez lire sur l'architecture du projet au début de son existence et quelles technologies ont été utilisées et pourquoi.


Le développement de la première version du service a pris environ un an. Pour déployer chaque composant du service, j'ai écrit des scripts dans Ansible . De temps en temps, le service ne fonctionnait pas en raison d'erreurs dans le code repensé ou d'une configuration incorrecte des composants.


Vers juin 2019, une erreur a été détectée dans le code de l'analyseur en raison de laquelle de nouvelles annonces n'ont pas été collectées. Au lieu d'une autre correction, il a été décidé de la désactiver temporairement.


La raison de la restauration du service était l'étude des k8.


Présentation des K8


k8s est un logiciel open source pour automatiser le déploiement, la mise à l'échelle et la gestion des applications conteneurisées.


L'ensemble de l'infrastructure de service est décrit par des fichiers de configuration au format yaml (le plus souvent).


Je ne parlerai pas de la structure interne des k8, mais ne donnerai que quelques informations sur certains de ses composants.


Composants K8s


  • Pod est la plus petite unité. Il peut contenir plusieurs conteneurs qui seront lancés sur le même nœud.
    Conteneurs à l'intérieur du pod:
    • ont un réseau commun et peuvent accéder les uns aux autres via 127.0.0.1:$containerPort;
    • n'ont pas de système de fichiers commun, vous ne pouvez donc pas écrire directement des fichiers d'un conteneur à un autre.
  • Déploiement - surveille le travail de Pod. Il peut augmenter le nombre requis d'instances de pod, les redémarrer si elles tombent et effectuer le déploiement de nouveaux pods.
  • PersistentVolumeClaim - entrepôt de données. Par défaut, il fonctionne avec le système de fichiers du nœud local. Par conséquent, si vous souhaitez que deux pods différents sur des nœuds différents aient un système de fichiers commun, vous devez utiliser un système de fichiers réseau comme Ceph .
  • Service - demandes de procuration vers et depuis Pod.
    Types de services:
    • LoadBalancer - pour l'interaction avec un réseau externe avec équilibrage de charge entre plusieurs pods;
    • NodePort (seulement 30000-32767 ports) - pour l'interaction avec un réseau externe sans équilibrage de charge;
    • ClusterIp - pour l'interaction dans le réseau local du cluster;
    • ExternalName - pour l'interaction entre Pod et les services externes.
  • ConfigMap - stockage des configurations.
    Pour que k8s redémarre Pod avec de nouvelles configurations lorsque ConfigMap change, vous devez indiquer la version dans le nom de votre ConfigMap et la changer chaque fois que ConfigMap change.
    Il en va de même pour Secret.

Exemple de configuration avec ConfigMap
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 

  • Secret - stockage des configurations secrètes (mots de passe, clés, jetons).
  • Étiquette - paires clé / valeur affectées aux composants k8s, par exemple Pod.
    Au début de la connaissance des k8, il peut ne pas être complètement clair comment utiliser les étiquettes. Voici la configuration qui explique les principes de base du travail avec les étiquettes:

Exemple de configuration avec des étiquettes
 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 

Se préparer au déménagement


Coupe fonctionnelle


Pour rendre le service plus stable et prévisible, j'ai dû supprimer tous les composants supplémentaires qui fonctionnaient mal et réécrire un peu les principaux.
J'ai donc décidé de refuser:


  • analyser le code pour les sites autres que VK;
  • demander un composant proxy;
  • composant des notifications de nouvelles annonces dans VKontakte et Telegram.

Composants de service


Après tous les changements, le service de l'intérieur a commencé à ressembler à ceci:
Schéma


  • vue - recherche et affichage d'annonces sur le site (NodeJS);
  • analyseur - classificateur d'annonces (Aller);
  • collector - collecte, traitement et suppression des publicités (PHP):
    • cron-explore - une équipe de console qui recherche des groupes sur VKontakte pour louer un logement;
    • cron-collect - une commande de console qui va aux groupes compilés par cron-explore et recueille les annonces elles-mêmes;
    • cron-delete - une commande de console qui supprime les annonces expirées;
    • consumer-parse - le gestionnaire de files d'attente, qui reçoit des tâches de cron-collect. Il classe les annonces à l'aide du composant analyseur;
    • consumer-collect - le gestionnaire de files d'attente qui obtient des travaux de consumer-parse. Il filtre les publicités incorrectes et en double.

Créer des images Docker


Afin de gérer les composants et de les surveiller dans un seul style, j'ai décidé:


  • mettre la configuration des composants dans des variables env,
  • écrire des journaux dans stdout.

Il n'y a rien de spécifique dans les images elles-mêmes.


Développement de configuration K8s


J'ai donc obtenu les composants dans les images Docker et j'ai commencé à développer la configuration k8s.


Tous les composants qui fonctionnent comme des démons sont mis en évidence dans Déploiement. Chaque démon doit être accessible au sein du cluster, donc tout le monde a un service. Toutes les tâches qui doivent être effectuées périodiquement fonctionnent dans CronJob.


Toutes les statistiques (images, js, css) sont stockées dans le conteneur de vue, et le conteneur Nginx doit le distribuer. Les deux conteneurs sont dans un seul pod. Le système de fichiers dans Pod n'est pas échappé, mais vous pouvez copier toutes les statistiques dans le dossier emptyDir commun aux deux conteneurs lors du démarrage de Pod. Ce dossier sera partagé pour différents conteneurs, mais uniquement à l'intérieur d'un pod.


Exemple de configuration avec emptyDir
 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"] 

Le composant collecteur est utilisé dans Deployment et CronJob.


Tous ces composants accèdent à l'API VKontakte et doivent stocker le jeton d'accès partagé quelque part.
Pour cela, j'ai utilisé PersistentVolumeClaim, que j'ai connecté à chaque Pod. Un tel dossier sera partagé pour différents pods, mais uniquement à l'intérieur d'un nœud.


Exemple de configuration avec PersistentVolumeClaim
 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 est également utilisé pour stocker des données de base de données. En conséquence, nous avons obtenu un tel schéma (les pods d'un composant sont collectés en blocs):


Schéma


Déploiement du cluster K8s


Pour commencer, j'ai déployé le cluster localement à l'aide de Minikube .
Bien sûr, il y a eu des erreurs, donc les équipes m'ont beaucoup aidé.


 kubectl logs -f pod-name kubectl describe pod pod-name 

Après avoir appris à déployer un cluster dans Minikube, il ne m'a pas été difficile de le déployer dans DigitalOcean.


En conclusion, je peux dire que le service est stable depuis 2 mois. La configuration complète peut être trouvée ici .

Source: https://habr.com/ru/post/fr484528/


All Articles