Nous accueillons 10 000 événements dans Yandex.Cloud. Partie 1

Bonjour à tous, amis !

* Cet article est basé sur le séminaire ouvert REBRAIN & Yandex.Cloud, si vous préférez regarder une vidéo, vous pouvez la trouver ici — https://youtu.be/cZLezUm0ekE

Récemment, nous avons eu l'occasion d'explorer Yandex.Cloud en direct. Comme nous voulions l'explorer en profondeur, nous avons immédiatement abandonné l'idée de lancer un simple blog Wordpress avec une base de données dans le cloud — trop ennuyeux. Après une brève réflexion, nous avons décidé de déployer quelque chose ressemblant à une architecture de production pour la réception et l'analyse d'événements en temps réel.

Je suis absolument convaincu que la grande majorité des entreprises en ligne (et pas seulement) collectent d'une manière ou d'une autre une multitude d'informations sur leurs utilisateurs et leurs actions. Au minimum, cela est nécessaire pour prendre certaines décisions — par exemple, si vous gérez un jeu en ligne, vous pouvez consulter les statistiques sur les niveaux où les utilisateurs échouent le plus souvent et désinstallent votre jeu. Ou pourquoi les utilisateurs quittent votre site sans rien acheter (salut, Yandex.Metrica).

Ainsi, notre histoire : comment nous avons écrit une application en golang, testé kafka vs rabbitmq vs yqs, écrit un streaming de données vers un cluster Clickhouse et visualisé les données avec yandex datalens. Bien sûr, tout cela a été assaisonné d'astuces infrastructurelles sous forme de docker, terraform, gitlab ci et, bien sûr, prometheus. Allons-y !

Tout d'abord, je tiens à préciser que nous ne pourrons pas tout configurer d'un seul coup — nous aurons besoin de plusieurs articles dans cette série. Un peu sur la structure :

Partie 1 (la vôtre). Nous définirons le cahier des charges et l'architecture de la solution, et nous écrirons l'application en golang.
Partie 2. Nous déployons notre application en production, la rendons évolutive et testons la charge.
Partie 3. Nous tenterons de comprendre pourquoi nous devons stocker les messages dans un tampon, et non dans des fichiers, ainsi que de comparer kafka, rabbitmq et yandex queue service.
Partie 4. Nous déploierons un cluster Clickhouse, écrire un streaming pour y transférer des données depuis le tampon, et configurer la visualisation dans datalens.
Partie 5. Nous mettrons toute l'infrastructure en ordre — configurer ci/cd en utilisant gitlab ci, intégrer la surveillance et la découverte de services à l'aide de prometheus et consul.

Cahier des charges

Tout d'abord, formulons le cahier des charges — ce que nous voulons exactement obtenir à la fin.

  1. Nous souhaitons avoir un endpoint de type events.kis.im (kis.im est un domaine de test que nous utiliserons tout au long des articles), qui doit recevoir des événements via HTTPS.
  2. Les événements sont un simple JSON du type : {«event»: «view», «os»: «linux», «browser»: «chrome»}. À l'étape finale, nous ajouterons quelques champs supplémentaires, mais cela n'a pas grande importance. Si vous le souhaitez, vous pouvez passer à protobuf.
  3. Le service doit être capable de traiter 10 000 événements par seconde.
  4. Il doit être possible de se mettre à l'échelle horizontalement — simplement en ajoutant de nouvelles instances à notre solution. Ce serait bien si nous pouvions déployer la partie frontale dans différentes géolocations pour réduire la latence lors des requêtes des clients.
  5. Résilience. La solution doit être suffisamment stable et capable de survivre à la défaillance de certaines parties (jusqu'à un certain nombre, bien sûr).

Architecture

En fait, pour ce type de tâches, des architectures classiques ont déjà été conçues, permettant une mise à l'échelle efficace. L'image ci-dessous représente un exemple de notre solution.

Nous accueillons 10 000 événements dans Yandex.Cloud. Partie 1

Alors, qu'avons-nous ?

1. À gauche se trouvent nos appareils qui génèrent divers événements, que ce soit le passage de niveaux de joueurs dans un jeu sur smartphone ou la création d'une commande dans un magasin en ligne via un navigateur classique. L'événement, comme indiqué dans le cahier des charges, est un simple JSON qui est envoyé à notre endpoint — events.kis.im.

2. Les deux premiers serveurs sont de simples équilibrateurs, leurs principales tâches sont :

  • Être toujours disponibles. Pour cela, nous pouvons utiliser, par exemple, keepalived, qui basculera l'IP virtuelle entre les nœuds en cas de problème.
  • Terminer le TLS. Oui, nous terminerons le TLS précisément sur eux. D'une part, pour que notre solution soit conforme au cahier des charges, et d'autre part, pour alléger le fardeau de l'établissement de la connexion chiffrée de nos serveurs backend.
  • Équilibrer les requêtes entrantes sur les serveurs backend disponibles. Le mot clé ici est disponible. À partir de cela, nous comprenons que les équilibrateurs de charge doivent être capables de surveiller nos serveurs d'applications et de cesser d'équilibrer le trafic vers les nœuds défaillants.

3. Derrière les équilibrateurs, nous avons des serveurs d'application, sur lesquels une application assez simple est exécutée. Elle doit être capable de recevoir des requêtes entrantes via HTTP, de valider le JSON envoyé et de stocker les données dans un tampon.

4. En tant que tampon, le schéma affiche Kafka, même si, bien sûr, à ce niveau, d'autres services similaires peuvent également être utilisés. Nous comparerons Kafka, RabbitMQ et YQS dans le troisième article.

5. L'avant-dernière étape de notre architecture est Clickhouse, une base de données en colonnes qui permet de stocker et de traiter d'énormes volumes de données. À ce niveau, nous devons transférer les données du tampon vers le système de stockage proprement dit (nous en parlerons dans l'article 4).

Ce schéma nous permet de faire évoluer horizontalement chaque couche de manière indépendante. Si les serveurs backend ne gèrent pas la charge, nous en ajoutons d'autres, car ils sont des applications sans état, ce qui signifie que cela peut être fait même en mode automatique. Si le tampon sous forme de Kafka n’est pas suffisant, nous ajoutons encore des serveurs et déplaçons certaines partitions de notre sujet. Si Clickhouse peine, c'est impossible 🙂 En réalité, nous ajoutons également des serveurs et effectuons le partitionnement des données.

D'ailleurs, si vous souhaitez réaliser la partie optionnelle de notre cahier des charges et mettre en place un évolutivité dans différentes géolocalisations, rien de plus simple :

Nous accueillons 10 000 événements dans Yandex.Cloud. Partie 1

Dans chaque géolocalisation, nous déployons un load balancer avec des applications et Kafka. En tout, il suffit de 2 serveurs d'application, 3 nœuds Kafka et un équilibrateur de charge cloud, par exemple, Cloudflare, qui vérifiera la disponibilité des nœuds d'application et équilibrera les requêtes par géolocalisation sur la base de l'adresse IP source du client. Ainsi, les données envoyées par un client américain atterriront sur des serveurs américains. Et les données en provenance d'Afrique seront sur des serveurs africains.

Ensuite, tout devient très simple : nous utilisons l'outil de miroir de la suite Kafka et nous copions toutes les données de tous les emplacements vers notre centre de données central situé en Russie. À l'intérieur, nous analysons les données et les écrivons dans Clickhouse pour une visualisation ultérieure.

Donc, nous avons compris l'architecture — commençons à tester Yandex.Cloud !

Nous écrivons une application

Avant de passer au Cloud, nous devons attendre un peu et écrire un service assez simple pour traiter les événements entrants. Nous allons utiliser Golang, car il s'est très bien illustré en tant que langage pour écrire des applications réseau.

Après avoir consacré une heure (peut-être même quelques heures) à ce projet, nous obtenons quelque chose de semblable : https://github.com/RebrainMe/yandex-cloud-events/blob/master/app/main.go.

Quels sont les points principaux que nous aimerions souligner ici :

1. Lors du démarrage de l'application, vous pouvez spécifier deux indicateurs. L'un est responsable du port sur lequel nous écouterons les requêtes HTTP entrantes (-addr). Le second indique l'adresse du serveur Kafka où nous écrirons nos événements (-kafka):

addr     = flag.String("addr", ":8080", "Adresse TCP à écouter")
kafka    = flag.String("kafka", "127.0.0.1:9092", "Points de terminaison Kafka")

2. L'application utilise la bibliothèque sarama ([] github.com/Shopify/sarama) pour envoyer des messages au cluster Kafka. Nous avons immédiatement défini des paramètres orientés vers une vitesse de traitement maximale :

config := sarama.NewConfig()
config.Producer.RequiredAcks = sarama.WaitForLocal
config.Producer.Compression = sarama.CompressionSnappy
config.Producer.Return.Successes = true

3. De plus, notre application intègre un client prometheus qui collecte diverses métriques, telles que :

  • le nombre de requêtes à notre application;
  • le nombre d'erreurs lors de l'exécution d'une requête (impossible de lire la requête POST, JSON corrompu, impossible d'écrire dans Kafka);
  • le temps de traitement d'une requête du client, y compris le temps nécessaire pour écrire le message dans Kafka.

4. Trois points d'extrémité que notre application traite :

  • /status — просто возвращаем ok, чтобы показать, что мы живы. Хотя можно и добавить некоторые проверки, типа доступности кафка кластера.
  • /metrics — по этому url prometheus client будет возвращать собранные им метрики.
  • /post — основной endpoint, куда будут приходить POST запросы с json внутри. Наше приложение проверяет json на валидность и если все ок — записывает данные в кафка-кластер.

Je précise que le code n'est pas parfait — il peut (et doit !) être amélioré. Par exemple, on peut se passer de l'utilisation de net/http intégré et passer à fasthttp, qui est plus rapide. Ou encore, optimiser le temps de traitement et les ressources CPU en décalant la validation JSON à une étape ultérieure — lorsque les données seront transférées du tampon au cluster ClickHouse.

En plus de l'aspect développement, nous avons immédiatement pensé à notre future infrastructure et avons décidé de déployer notre application via Docker. Le Dockerfile final pour la construction de l'application — https://github.com/RebrainMe/yandex-cloud-events/blob/master/app/Dockerfile. Dans l'ensemble, il est assez simple, le seul point sur lequel je tiens à attirer l'attention est la construction multistage, qui permet de réduire l'image finale de notre conteneur.

Premiers pas dans le cloud

La première étape consiste à s'inscrire sur cloud.yandex.ru. Après avoir rempli tous les champs nécessaires, un compte sera créé pour vous et un crédit d'un certain montant vous sera attribué, que vous pourrez utiliser pour tester les services cloud. Si vous souhaitez reproduire toutes les étapes de notre article, ce crédit devrait suffire.

Après l'inscription, un cloud séparé et un catalogue par défaut seront créés pour vous, dans lequel vous pourrez commencer à créer des ressources cloud. En général, dans Yandex.Cloud, la relation entre les ressources est présentée comme suit :

Nous accueillons 10 000 événements dans Yandex.Cloud. Partie 1

Pour un compte, vous pouvez créer plusieurs nuages. À l'intérieur du nuage, vous pouvez créer différents répertoires pour différents projets de l'entreprise. Vous pouvez lire plus à ce sujet dans la documentation — https://cloud.yandex.ru/docs/resource-manager/concepts/resources-hierarchy. D'ailleurs, je vais souvent y faire référence dans le texte ci-dessous. Lorsque j'ai configuré toute l'infrastructure à partir de zéro, la documentation m'a été d'une grande aide, je vous conseille donc de l'étudier.

Pour gérer le nuage, vous pouvez utiliser à la fois l'interface web et l'outil en ligne de commande — yc. L'installation s'effectue avec une seule commande (pour Linux et Mac OS) :

curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash

Si votre conscience de sécurité vous empêche de lancer des scripts depuis Internet, d'une part, vous pouvez ouvrir le script et le lire, et d'autre part, nous l'exécutons sous notre propre utilisateur — sans droits root.

Si vous souhaitez installer le client pour Windows, vous pouvez suivre les instructions ici puis exécuter yc init, pour le configurer complètement :

vozerov@mba:~ $ yc init
Bienvenue ! Cette commande vous guidera à travers le processus de configuration.
Veuillez vous rendre sur https://oauth.yandex.ru/authorize?response_type=token&client_id= pour obtenir le token OAuth.

Veuillez entrer le token OAuth :
Veuillez choisir le nuage à utiliser :
 [1] cloud-b1gv67ihgfu3bp (id = b1gv67ihgfu3bpt24o0q)
 [2] fevlake-cloud (id = b1g6bvup3toribomnh30)
Veuillez entrer votre choix numérique : 2
Votre nuage actuel a été défini sur 'fevlake-cloud' (id = b1g6bvup3toribomnh30).
Veuillez choisir le répertoire à utiliser :
 [1] default (id = b1g5r6h11knotfr8vjp7)
 [2] Créer un nouveau répertoire
Veuillez entrer votre choix numérique : 1
Votre répertoire actuel a été défini sur 'default' (id = b1g5r6h11knotfr8vjp7).
Souhaitez-vous configurer une zone de calcul par défaut ? [Y/n]
Quelle zone voulez-vous utiliser comme par défaut ?
 [1] ru-central1-a
 [2] ru-central1-b
 [3] ru-central1-c
 [4] Ne pas définir de zone par défaut
Veuillez entrer votre choix numérique : 1
Votre zone de calcul par défaut a été définie sur 'ru-central1-a'.
vozerov@mba:~ $

En principe, le processus n'est pas compliqué — vous devez d'abord obtenir un token OAuth pour gérer le nuage, choisir le nuage et le répertoire que vous allez utiliser.

Si vous avez plusieurs comptes ou répertoires dans un seul nuage, vous pouvez créer des profils supplémentaires avec des paramètres distincts via yc config profile create et passer de l'un à l'autre.

En plus des méthodes mentionnées ci-dessus, l'équipe de Yandex.Cloud a écrit un très bon plugin pour terraform pour gérer les ressources cloud. Pour ma part, j'ai préparé un dépôt git où j'ai décrit toutes les ressources qui seront créées dans le cadre de cet article — https://github.com/rebrainme/yandex-cloud-events/. Nous sommes intéressés par la branche master, clonons-la localement :


vozerov@mba:~ $ git clone https://github.com/rebrainme/yandex-cloud-events/ events
Cloning into 'events'...
remote: Enumerating objects: 100, done.
remote: Counting objects: 100% (100/100), done.
remote: Compressing objects: 100% (68/68), done.
remote: Total 100 (delta 37), reused 89 (delta 26), pack-reused 0
Receiving objects: 100% (100/100), 25.65 KiB | 168.00 KiB/s, done.
Resolving deltas: 100% (37/37), done.
vozerov@mba:~ $ cd events/terraform/

Toutes les variables principales utilisées dans terraform sont spécifiées dans le fichier main.tf. Pour commencer, nous créons dans le dossier terraform le fichier private.auto.tfvars avec le contenu suivant :

# Yandex Cloud Oauth token
yc_token = ""
# Yandex Cloud ID
yc_cloud_id = ""
# Yandex Cloud folder ID
yc_folder_id = ""
# Default Yandex Cloud Region
yc_region = "ru-central1-a"
# Cloudflare email
cf_email = ""
# Cloudflare token
cf_token = ""
# Cloudflare zone id
cf_zone_id = ""

Toutes les variables peuvent être récupérées à partir de yc config list, car nous avons déjà configuré l'outil en ligne de commande. Je recommande d'ajouter immédiatement private.auto.tfvars dans .gitignore, afin de ne pas publier accidentellement des données privées.

Dans private.auto.tfvars, nous avons également spécifié les données de Cloudflare – pour la création d'enregistrements DNS et le proxy du domaine principal events.kis.im vers nos serveurs. Si vous ne souhaitez pas utiliser Cloudflare, supprimez l'initialisation du fournisseur Cloudflare dans main.tf et le fichier dns.tf, qui est responsable de la création des enregistrements DNS nécessaires.

Dans notre travail, nous allons combiner les trois méthodes – l'interface web, l'outil en ligne de commande et terraform.

Réseaux virtuels

Honnêtement, cette étape pourrait être omise, car lors de la création d'un nouveau cloud, un réseau distinct et 3 sous-réseaux sont automatiquement créés – un pour chaque zone de disponibilité. Mais nous souhaitons tout de même créer pour notre projet un réseau distinct avec sa propre adressage. Le schéma général de fonctionnement du réseau dans Yandex.Cloud est présenté sur l'illustration ci-dessous (prise honnêtement sur https://cloud.yandex.ru/docs/vpc/concepts/)

Nous accueillons 10 000 événements dans Yandex.Cloud. Partie 1

Ainsi, vous créez un réseau commun, à l'intérieur duquel les ressources pourront communiquer entre elles. Pour chaque zone de disponibilité, un sous-réseau avec son propre adressage est créé et connecté au réseau commun. En fin de compte, toutes les ressources cloud en elle peuvent communiquer, même en étant dans différentes zones de disponibilité. Les ressources connectées à différents réseaux cloud peuvent se voir uniquement à travers les adresses externes. Au fait, comment cette magie fonctionne à l'intérieur, a été bien décrite sur Habr.

La création du réseau est décrite dans le fichier network.tf du dépôt. Là, nous créons un réseau privé commun internal et connectons trois sous-réseaux dans différentes zones de disponibilité – internal-a (172.16.1.0/24), internal-b (172.16.2.0/24), internal-c (172.16.3.0/24).

Initialisons terraform et créons des réseaux :

vozerov@mba:~\/events\/terraform (master) $ terraform init
... ignoré ..

vozerov@mba:~\/events\/terraform (master) $ terraform apply -target yandex_vpc_subnet.internal-a -target yandex_vpc_subnet.internal-b -target yandex_vpc_subnet.internal-c

... ignoré ...

Plan : 4 à ajouter, 0 à modifier, 0 à détruire.

Voulez-vous effectuer ces actions ?
  Terraform effectuera les actions décrites ci-dessus.
  Seul 'oui' sera accepté pour approuver.

  Entrez une valeur : oui

yandex_vpc_network.internal : Création...
yandex_vpc_network.internal : Création terminée après 3s [id=enp2g2rhile7gbqlbrkr]
yandex_vpc_subnet.internal-a : Création...
yandex_vpc_subnet.internal-b : Création...
yandex_vpc_subnet.internal-c : Création...
yandex_vpc_subnet.internal-a : Création terminée après 6s [id=e9b1dad6mgoj2v4funog]
yandex_vpc_subnet.internal-b : Création terminée après 7s [id=e2liv5i4amu52p64ac9p]
yandex_vpc_subnet.internal-c : Toujours en création... [10s écoulés]
yandex_vpc_subnet.internal-c : Création terminée après 10s [id=b0c2qhsj2vranoc9vhcq]

Application terminée ! Ressources : 4 ajoutées, 0 modifiées, 0 détruites.

Parfait ! Nous avons créé notre réseau et sommes maintenant prêts à créer nos services internes.

Création de machines virtuelles

Pour tester l'application, il suffira de créer deux machines virtuelles : la première sera nécessaire pour la compilation et l'exécution de l'application, la seconde pour exécuter kafka, que nous utiliserons pour stocker les messages entrants. Et nous allons créer une autre machine pour configurer prometheus afin de surveiller l'application.

Les machines virtuelles seront configurées à l'aide d'ansible, donc avant de lancer terraform, assurez-vous d'avoir l'une des dernières versions d'ansible. Et installez les rôles nécessaires avec ansible galaxy :

vozerov@mba:~\/events\/terraform (master) $ cd ..\/ansible\/ 
vozerov@mba:~\/events\/ansible (master) $ ansible-galaxy install -r requirements.yml
- cloudalchemy-prometheus (master) est déjà installé, passez.
- cloudalchemy-grafana (master) est déjà installé, passez.
- sansible.kafka (master) est déjà installé, passez.
- sansible.zookeeper (master) est déjà installé, passez.
- geerlingguy.docker (master) est déjà installé, passez.
vozerov@mba:~\/events\/ansible (master) $

Dans le dossier ansible, il y a un exemple de fichier de configuration .ansible.cfg que j'utilise. Cela pourrait être utile.

Avant de créer des machines virtuelles, assurez-vous que le ssh-agent est en cours d'exécution et que votre clé ssh est ajoutée, sinon terraform ne pourra pas se connecter aux machines créées. Bien sûr, j'ai rencontré un bogue sur os x : https://github.com/ansible/ansible/issues/32499#issuecomment-341578864. Pour éviter que cela ne se reproduise, avant de lancer Terraform, ajoutez une petite variable dans l'env :

vozerov@mba:~\/events\/terraform (master) $ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

Dans le dossier terraform, nous créons les ressources nécessaires :

vozerov@mba:~\/events\/terraform (master) $ terraform apply -target yandex_compute_instance.build -target yandex_compute_instance.monitoring -target yandex_compute_instance.kafka
yandex_vpc_network.internal: Actualisation de l'état... [id=enp2g2rhile7gbqlbrkr]
data.yandex_compute_image.ubuntu_image: Actualisation de l'état...
yandex_vpc_subnet.internal-a: Actualisation de l'état... [id=e9b1dad6mgoj2v4funog]

Un plan d'exécution a été généré et est présenté ci-dessous.
Les actions sur les ressources sont indiquées par les symboles suivants :
  + créer

... sauté ...

Plan : 3 à ajouter, 0 à changer, 0 à détruire.

... sauté ...

Si tout s'est bien passé (et cela devrait être le cas), nous aurons trois machines virtuelles :

  1. build — machine pour le test et la construction de l'application. Docker a été installé automatiquement par ansible.
  2. monitoring — machine de surveillance — elle a prometheus & grafana installés. Login / mot de passe par défaut : admin / admin
  3. kafka — petite machine avec kafka installée, accessible sur le port 9092.

Vérifions qu'elles sont toutes présentes :

vozerov@mba:~\/events (master) $ yc compute instance list
+----------------------+------------+---------------+---------+---------------+-------------+
|          ID          |    NOM     |    ID DE ZONE  | ÉTAT   |  IP EXTERNE   | IP INTERNE  |
+----------------------+------------+---------------+---------+---------------+-------------+
| fhm081u8bkbqf1pa5kgj | monitoring | ru-central1-a | EN MARCHE | 84.201.159.71 | 172.16.1.35 |
| fhmf37k03oobgu9jmd7p | kafka      | ru-central1-a | EN MARCHE | 84.201.173.41 | 172.16.1.31 |
| fhmt9pl1i8sf7ga6flgp | build      | ru-central1-a | EN MARCHE | 84.201.132.3  | 172.16.1.26 |
+----------------------+------------+---------------+---------+---------------+-------------+

Les ressources sont là, et à partir de là, nous pouvons extraire leurs adresses IP. Partout ailleurs, j'utiliserai les adresses IP pour me connecter par ssh et tester l'application. Si vous avez un compte sur Cloudflare connecté à Terraform, n'hésitez pas à utiliser les noms DNS fraîchement créés.
Au fait, lors de la création de la machine virtuelle, une IP interne et un nom DNS interne sont attribués, donc on peut accéder aux serveurs à l'intérieur du réseau par leurs noms :

ubuntu@build:~$ ping kafka.ru-central1.internal
PING kafka.ru-central1.internal (172.16.1.31) 56(84) octets de données.
64 octets de kafka.ru-central1.internal (172.16.1.31) : icmp_seq=1 ttl=63 time=1.23 ms
64 octets de kafka.ru-central1.internal (172.16.1.31) : icmp_seq=2 ttl=63 time=0.625 ms
^C
--- statistiques de ping kafka.ru-central1.internal ---
2 paquets transmis, 2 reçus, 0% de perte, temps 1001ms
rtt min/avg/max/mdev = 0.625/0.931/1.238/0.308 ms

Ceci nous sera utile pour spécifier à l'application le point de terminaison avec kafka.

Construisons l'application

Super, les serveurs sont là, l'application est là — il ne reste plus qu'à la construire et à la publier. Pour la construction, nous utiliserons une construction docker ordinaire, et comme stockage d'images, nous prendrons le service de Yandex : container registry. Mais tout cela dans l'ordre.

Nous copions l'application sur la machine build, nous nous connectons par ssh et nous construisons l'image :

vozerov@mba:~\/events\/terraform (master) $ cd ..
vozerov@mba:~\/events (master) $ rsync -av app\/ ubuntu@84.201.132.3:app\/\n\n... skipped ...\n\nsent 3849 bytes  received 70 bytes  7838.00 bytes\/sec
total size is 3644  speedup is 0.93\n\nvozerov@mba:~\/events (master) $ ssh 84.201.132.3 -l ubuntu
ubuntu@build:~$ cd app
ubuntu@build:~\/app$ sudo docker build -t app .\nSending build context to Docker daemon  6.144kB\nStep 1\/9 : FROM golang:latest AS build\n... skipped ...\n\nSuccessfully built 9760afd8ef65\nSuccessfully tagged app:latest

La moitié du travail est faite — maintenant, nous pouvons vérifier le bon fonctionnement de notre application en la lançant et en la dirigeant vers kafka :

ubuntu@build:~\/app$ sudo docker run --name app -d -p 8080:8080 app \/app\/app -kafka=kafka.ru-central1.internal:9092\n\nDepuis la machine locale, nous pouvons envoyer un événement de test et voir la réponse :\n\nvozerov@mba:~\/events (master) $ curl -D - -s -X POST -d '{"key1":"data1"}' http:\/\/84.201.132.3:8080\/post\nHTTP\/1.1 200 OK\nContent-Type: application\/json\nDate: Mon, 13 Apr 2020 13:53:54 GMT\nContent-Length: 41\n\n{"status":"ok","partition":0,"Offset":0}\nvozerov@mba:~\/events (master) $

L'application a répondu avec succès à l'enregistrement en indiquant l'id de la partition et l'offset dans lequel le message a été reçu. Il ne reste plus qu'à créer un registre dans Yandex.Cloud et à y pousser notre image (comment le faire en trois lignes est décrit dans le fichier registry.tf). Créons le stockage :

vozerov@mba:~\/events\/terraform (master) $ terraform apply -target yandex_container_registry.events\n\n... skipped ...\n\nPlan : 1 à ajouter, 0 à changer, 0 à détruire.\n\n... skipped ...\n\nApplication terminée ! Ressources : 1 ajoutée, 0 modifiée, 0 détruite.

Il existe plusieurs façons de s'authentifier dans le registre de conteneurs — à l'aide d'un jeton oauth, d'un jeton iam ou d'une clé de compte de service. Pour plus de détails sur ces méthodes, consultez la documentation. https://cloud.yandex.ru/docs/container-registry/operations/authentication. Nous allons utiliser la clé de compte de service, donc nous créons un compte :

vozerov@mba:~\/events\/terraform (master) $ terraform apply -target yandex_iam_service_account.docker -target yandex_resourcemanager_folder_iam_binding.puller -target yandex_resourcemanager_folder_iam_binding.pusher\n\n... skipped ...\n\nApplication terminée ! Ressources : 3 ajoutées, 0 modifiées, 0 détruites.

Il ne nous reste plus qu'à créer la clé :

vozerov@mba:~\/events\/terraform (master) $ yc iam key create --service-account-name docker -o key.json\nid: ajej8a06kdfbehbrh91p\nservice_account_id: ajep6d38k895srp9osij\ncreated_at: "2020-04-13T14:00:30Z"\nkey_algorithm: RSA_2048

Obtenons l'information sur l'id de notre stockage, transférons la clé et nous connectons :

vozerov@mba:~\/events\/terraform (master) $ scp key.json ubuntu@84.201.132.3:\nkey.json                                                                                                                    100% 2392   215.1KB\/s   00:00\n\nvozerov@mba:~\/events\/terraform (master) $ ssh 84.201.132.3 -l ubuntu\n\nubuntu@build:~$ cat key.json | sudo docker login --username json_key --password-stdin cr.yandex\nWARNING! Your password will be stored unencrypted in \/home\/ubuntu\/.docker\/config.json.\nConfigure a credential helper to remove this warning. See\nhttps:\/\/docs.docker.com\/engine\/reference\/commandline\/login\/#credentials-store\n\nLogin Succeeded\nubuntu@build:~$

Pour télécharger l'image dans le registre, nous aurons besoin de l'ID du registre de conteneurs, nous le prenons à partir de l'outil yc :

vozerov@mba:~ $ yc conteneur registre get événements
id: crpdgj6c9umdhgaqjfmm
folder_id:
nom: événements
statut: ACTIF
créé_le: "2020-04-13T13:56:41.914Z"

Après cela, nous taguons notre image avec un nouveau nom et la téléchargeons :

ubuntu@build:~$ sudo docker tag app cr.yandex/crpdgj6c9umdhgaqjfmm/events:v1
ubuntu@build:~$ sudo docker push cr.yandex/crpdgj6c9umdhgaqjfmm/events:v1
Le push fait référence au dépôt [cr.yandex/crpdgj6c9umdhgaqjfmm/events]
8c286e154c6e: Poussé
477c318b05cb: Poussé
beee9f30bc1f: Poussé
v1: digest: sha256:1dd5aaa9dbdde2f60d833be0bed1c352724be3ea3158bcac3cdee41d47c5e380 taille: 946

Nous pouvons vérifier que l'image a bien été téléchargée :

vozerov@mba:~/events/terraform (master) $ yc conteneur dépôt liste
+----------------------+-----------------------------+
|          ID          |            NOM              |
+----------------------+-----------------------------+
| crpe8mqtrgmuq07accvn | crpdgj6c9umdhgaqjfmm/events |
+----------------------+-----------------------------+

Au fait, si vous installez l'utilitaire yc sur une machine Linux, vous pouvez utiliser la commande

yc conteneur registre configurer-docker

pour configurer Docker.

Conclusion

Nous avons fait un grand travail difficile et en conséquence :

  1. Nous avons conçu l'architecture de notre futur service.
  2. Nous avons écrit une application en golang qui met en œuvre notre logique métier.
  3. Nous l'avons assemblée et déployée dans un registre de conteneurs privé.

Dans la prochaine partie, nous passerons à quelque chose d'intéressant : nous déploierons notre application en production et enfin, nous effectuerons une charge. Ne changez pas de chaîne !

Ce matériel est disponible en vidéo de l'atelier ouvert REBRAIN & Yandex.Cloud : Nous recevons 10 000 requêtes par seconde sur Yandex Cloud — https://youtu.be/cZLezUm0ekE

Si vous êtes intéressé à assister à de tels événements en ligne et à poser des questions en temps réel, rejoignez-nous sur la chaîne DevOps by REBRAIN.

Nous tenons à remercier Yandex.Cloud pour la possibilité d'organiser un tel événement. Voici leur lien — https://cloud.yandex.ru/prices

Si vous avez besoin de passer au cloud ou si vous avez des questions sur votre infrastructure, n'hésitez pas à laisser une demande.

P.S. Nous avons 2 audits gratuits par mois, peut-être que votre projet sera l'un d'eux.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster