Cet article décrit la création d'une application de type microservice avec Python, MySQL et Docker et son déploiement sur Fargate.
Je voulais utiliser Fargate, qui est considéré comme le plus populaire (en partie) sur AWS, et j'étais intéressé par l'architecture des microservices. Cependant, comme je n'ai jamais traité correctement des micro-services, c'est une architecture délirante que "je me demande si ça ressemble à ça". Si vous avez des erreurs, nous vous serions reconnaissants de bien vouloir nous donner votre avis.
De plus, AWS a RDS, mais comme le conteneur est également un débutant, j'utilise également le conteneur MySQL pour étudier.
Il existe de nombreuses explications sur le net, mais je vais brièvement décrire ma compréhension.
Fargate
Telle est la compréhension. En d'autres termes, cette fois, j'essaierai de déployer le micro service construit avec __Docker sur Fargate, qui est le service de gestion d'AWS __.
--Il existe deux micro-services, Apl et BackEnd.
Commencez par déployer le microservice localement en tant qu'hôte, puis apportez-le à Fargate. Après un déploiement réussi sur Fargate, l'objectif est de pouvoir communiquer avec les deux microservices de l'extérieur via HTTP.
Déployez l'application sur votre Mac local. Les sources, etc. sont les suivantes.
Créez des dossiers pour chacun des trois conteneurs (apl-service, db-service, mysql).
.
├── apl-service
│ ├── Dockerfile
│ └── src
│ ├── results.py
│ └── server.py
├── db-service
│ ├── Dockerfile
│ └── src
│ ├── server.py
│ └── students.py
├── docker-compose.yml
└── mysql
├── Dockerfile
└── db
├── mysql_data
└── mysql_init
└── setup.sql
mysql/Dockerfile
FROM mysql/mysql-server:5.7
RUN chown -R mysql /var/lib/mysql && \
chgrp -R mysql /var/lib/mysql
setup.sql et les données réellement enregistrées
create table students (id varchar(4), name varchar(20), score int);
insert into students values ('1001', 'Alice', 60);
insert into students values ('1002', 'Bob', 80);
commit;
mysql> select * from DB01.students;
+------+-------+-------+
| id | name | score |
+------+-------+-------+
| 1001 | Alice | 60 |
| 1002 | Bob | 80 |
+------+-------+-------+
db-service/Dockerfile
FROM python:3.6
#Travailler DIR sur le conteneur
WORKDIR /usr/src/
#Installation de la bibliothèque
RUN pip install flask mysql-connector-python
CMD python ./server.py
db-service/src/server.py
from flask import Flask, request, abort, render_template, send_from_directory
from students import get_students
app = Flask(__name__)
@app.route('/students', methods=['GET'])
def local_endpoint():
return get_students()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)
db-service/src/students.py
import json
import mysql.connector as mydb
def get_students():
conn = mydb.connect(user="user", passwd="password",
host="mysql", port="3306")
cur = conn.cursor()
sql_qry = 'select id, name, score from DB01.students;'
cur.execute(sql_qry)
rows = cur.fetchall()
results = [{"id": i[0], "name": i[1], "score": i[2]} for i in rows]
return_json = json.dumps({"students": results})
cur.close()
conn.close()
return return_json
/ Students``` est accédée par GET.
--get_students se connecte au conteneur MySQL et stocke les résultats sélectionnés dans la base de données en lignes. À partir des lignes, il est converti en dict en notation d'inclusion, puis converti en json et la valeur est renvoyée.links
. Ou plutôt, links
semble être une vieille technique qui n'a pas été utilisée très récemment ...apl-service/Dockerfile
FROM python:3.8
#Travailler DIR sur le conteneur
WORKDIR /usr/src/
#Installation de la bibliothèque
RUN pip install requests flask
CMD python ./server.py
apl-service/src/server.py
from flask import Flask, request, abort, render_template, send_from_directory
from results import get_results
API_URI = 'http://db-service:5001/students'
app = Flask(__name__)
@app.route('/results', methods=['GET'])
def local_endpoint():
return get_results(API_URI)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5002)
apl-service/src/results.py
import json
import requests
def get_results(uri):
r = requests.get(uri)
students = r.json()["students"]
return_students = []
for student in students:
if student["score"] > 70:
student.setdefault("isPassed", True)
return_students.append(student)
else:
student.setdefault("isPassed", False)
return_students.append(student)
return_json = json.dumps({"addedStudents": return_students})
return return_json
/ results``` est accédée par GET." isPassed "
sur True si la valeur de chaque score est supérieure à 70, False sinon, et ajoutez l'élément json.docker-compose
docker-compose.yml
version: '3'
services:
mysql:
container_name: mysql
build:
context: .
dockerfile: ./mysql/Dockerfile
hostname: mysql
ports:
- "3306:3306"
volumes:
- ./mysql/db/mysql_init:/docker-entrypoint-initdb.d
- ./mysql/db/mysql_data:/var/lib/mysql
environment:
MYSQL_USER: user
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: DB01
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --skip-character-set-client-handshake
db-service:
build:
context: .
dockerfile: ./db-service/Dockerfile
container_name: db-service
ports:
- "5001:5001"
volumes:
- ./db-service/src/:/usr/src/
apl-service:
build:
context: .
dockerfile: ./apl-service/Dockerfile
container_name: apl-service
ports:
- "5002:5002"
volumes:
- ./apl-service/src/:/usr/src/
Je vais omettre le résultat, mais le déployer avec la commande docker-compose.
build&up
$ docker-compose build
$ docker-compose up
Une fois le déploiement réussi, essayez d'accéder à chaque microservice via HTTP pour confirmer la communication. Comme vous pouvez le voir à partir de la source, db-service utilise les ports 5001 et apl-service utilise les ports 5002.
db-Service d'accès
$ curl http://127.0.0.1:5001/students
{"students": [{"id": "1001", "name": "Alice", "score": 60}, {"id": "1002", "name": "Bob", "score": 80}]}
apl-Service d'accès
$ curl http://127.0.0.1:5002/results
{"addedStudents": [{"id": "1001", "name": "Alice", "score": 60, "isPassed": false}, {"id": "1002", "name": "Bob", "score": 80, "isPassed": true}]}
Vous avez maintenant déployé avec succès le microservice sous-jacent (délirant) localement. Étant donné que le résultat de l'API du service apl est renvoyé, vous pouvez également confirmer qu'il existe une communication entre les microservices apl-service et db-service.
Je vais apporter ça à Fargate tout de suite! Mais cela faisait longtemps d'ici ...
Il s'agit d'un diagramme de configuration de la version finale basée sur le déploiement sur Fargate.
Les points sont les suivants.
―― 1 Déploiement de microservices sur un Fargate. --Il y a une communication entre les conteneurs (db-service ⇔ mysql) dans la tâche. --Il y a communication inter-tâches (service apl ⇔ service db).
Sur cette base, nous allons déployer.
Tout d'abord, poussez les trois images docker créées (service apl, service db, mysql) vers ECR. L'interface utilisateur d'ECR est facile à comprendre, donc si vous appuyez sur «Créer un référentiel» à partir de la console AWS et que vous procédez tel quel, vous pouvez le pousser.
docker rm
.Avec ECS, vous pouvez utiliser docker-compose.yml. Après avoir défini ecs-cli, suivez le tutoriel ci-dessous pour déployer sur Fargate, mais vous devez modifier docker-compose.yml etc. Je vais soulever divers points de correction, mais la source de la version finale est publiée à la fin de l'article, donc si vous êtes occupé, veuillez vous y référer.
Tutoriel: Créer un cluster de tâches Fargate à l'aide de l'interface de ligne de commande Amazon ECS
De plus, dans cette application, nous utilisons également les ports 5001, 5002, nous devons donc les autoriser dans les paramètres du groupe de sécurité à l'étape 3.
Lors de l'autorisation de 5001
$ aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 5001 --cidr 0.0.0.0/0 --region <region>
Exemple) Dossier "aws_Stocker dans "travail" et déployer avec le backend de nom de tâche
$ pwd
/Users/<réduction>/aws_work
$ ls docker-compose.yml # db-service +docker mysql-compose.yml
docker-compose.yml
$ ecs-cli compose --project-name backend service up ~
Exemple) Dossier "aws_Stocker dans "travail" et déployer avec le nom de tâche apl
$ pwd
/Users/<réduction>/aws_work2
$ ls docker-compose.yml # apl-service docker-compose.yml
docker-compose.yml
$ ecs-cli compose --project-name apl service up ~
build
, container_name
et hostname
ne sont pas obligatoires, spécifiez ʻimage` à la place.python-mysql / mysql
.
--Remplacez également db-service et apl-service par ʻimage`.Exemple) docker-compose.points de correction yml(paramètres d'image)
mysql:
image: <aws_account_id>.dkr.ecr.<region>.amazonaws.com/python-mysql/mysql
--Fargate ne prend pas en charge les volumes de stockage persistants. Cela semble difficile, mais cela signifie que vous ne pouvez pas utiliser de volumes dans docker-compose.yml. Par conséquent, la source telle que server.py n'est pas reflétée dans le conteneur. --Ainsi, commentez (ou supprimez physiquement) les volumes de docker-compose.yml et modifiez chacun d'eux pour inclure COPY dans le Dockerfile. --db-service et apl-service apportent des modifications similaires.
Exemple) docker-compose.points de correction yml
# volumes:
# - ./db-service/src/:/usr/src/
Exemple) db-service/Points de modification du Dockerfile
#Copiez la source dans le conteneur
COPY ./src/server.py /usr/src/server.py
COPY ./src/students.py /usr/src/students.py
Exemple) docker-compose.points de correction yml(Journal du conteneur)
mysql:
logging:
driver: awslogs
options:
awslogs-group: python-docker
awslogs-region: <region>
awslogs-stream-prefix: mysql
Notez également que, bien que non résolu cette fois, Fargate exige que les ports
aient le même numéro sur les côtés hôte et conteneur. Par exemple, vous pouvez spécifier «80: 5001» localement, mais vous obtiendrez une erreur si vous ne spécifiez pas «5001: 5001» dans Fargate.
J'ai fixé environ 4 points, mais cela ne fonctionne toujours pas.
À partir de là, il s'agit d'une modification nécessaire pour réaliser la communication inter-conteneurs mentionnée ci-dessus dans la tâche et la communication inter-tâches. Tout d'abord, afin de réaliser la communication entre les conteneurs, nous décrirons db-service et mysql.
Après avoir déployé le correctif jusqu'à présent, si vous vérifiez l'état avec la commande ```ecs-cli compose ~ service ps ~ `` , il sera
RUNNING` pour le moment.
En fait, c'est le résultat de ps après le démarrage.
$ ecs-cli compose --project-name backend service ps --cluster-config sample-config --ecs-profile sample-profile
Name State Ports TaskDefinition Health
<task-id>/db-service RUNNING XXX.XXX.XXX.XXX:5001->5001/tcp backend:12 UNKNOWN
<task-id>/mysql RUNNING XXX.XXX.XXX.XXX:3306->3306/tcp backend:12 UNKNOWN
Puisque l'adresse IP de Globus est affichée, j'essaie de communiquer avec le service db de Mac par GET, mais comme mentionné ci-dessus, cela ne fonctionne toujours pas.
$ curl http://XXX.XXXX.XXX.XXX:5001/students
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
Comme il se trouve sur la console ECS, si vous vérifiez le journal sur l'onglet Journaux, etc., vous obtiendrez une erreur indiquant que le nom ne peut pas être résolu. Dans ce cas, j'obtiens une erreur indiquant que le nom d'hôte de mysql ne peut pas être trouvé à partir de db-service. Le fait est qu'il n'y a pas de communication entre les conteneurs. Je l'ai écrit lors du déploiement local, mais cette fois je n'utilise pas de liens. En outre, le type Fargate ne prend pas en charge les liens en premier lieu.
Ensuite, comment le résoudre, c'est que la communication inter-conteneurs de Fargate est accessible par numéro de port si elle est sur la même tâche. Modifiez donc la source de student.py comme suit. De plus, comme le codage du nom d'hôte est subtil, je vais le modifier pour qu'il soit passé en tant que variable d'environnement à partir de docker-compose.yml.
db-service/src/students.points fixes py
import json
import mysql.connector as mydb
import os #ajouter à
def get_students():
#Correction pour obtenir de la variable d'environnement
conn = mydb.connect(user=os.environ['DB_USER'], passwd=os.environ['DB_PASS'],
host=os.environ['DB_HOST'], port=os.environ['DB_PORT'])
docker-compose.points de correction yml(db-service)
db-service:
-réduction-
environment:
DB_HOST: 127.0.0.1
DB_USER: "user"
DB_PASS: "password"
DB_PORT: "3306"
Poussez à nouveau sur ECR et déployez.
Si vous avez déployé sur Fargate une fois, pour refléter les changements, supprimez-le une fois avec la commande ```ecs-cli compose ~ service down ~ `dans le didacticiel, et` `ʻecs-cli compose Redémarrez avec la commande ~ service up ~
``.
Vous pouvez confirmer que la communication HTTP du Mac vers le service db est réussie. Voici un aperçu des voies de communication.
$ curl http://XXX.XXX.XXX.XXX:5001/students
{"students": [{"id": "1001", "name": "Alice", "score": 60}, {"id": "1002", "name": "Bob", "score": 80}]}
Ensuite, laissez le service apl communiquer. Comme mentionné précédemment, apl-service exécute l'API db-service, il doit donc communiquer avec une autre tâche. Pour y parvenir avec Fargate, activez la détection et le lancement du service. Le mécanisme de détection de service est omis car l'article suivant de Classmethod est très facile à comprendre, mais en bref, l'enregistrement A de Route53 est automatiquement créé entre les microservices et la détection de nom est possible.
Puisque nous utilisons ecs-cli cette fois, nous allons le déployer avec des commandes en nous référant au didacticiel officiel AWS suivant.
[Tutoriel: Créer un service Amazon ECS qui utilise la découverte de services à l'aide de l'interface de ligne de commande Amazon ECS](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-cli-tutorial-servicediscovery .html)
Le nom d'hôte lorsque le service est détecté est service_name.namespace
. Cette fois, le nom du service est créé en tant que backend et l'espace de noms est créé en tant qu'exemple, donc pour accéder à db-service depuis apl-service, il est nécessaire de définir le nom d'hôte backend.sample
.
Ainsi, comme avec db-service, modifiez-le pour définir le nom d'hôte à partir de la variable d'environnement.
apl-service/src/server.points fixes py
from flask import Flask, request, abort, render_template, send_from_directory
from results import get_results
import os #ajouter à
#Correction pour obtenir de la variable d'environnement
API_URI = 'http://' + os.environ['BACKEND_HOST'] +':' + os.environ['BACKEND_PORT'] + '/students'
docker-compose.points de correction yml(apl-service)
apl-service:
-réduction-
environment:
BACKEND_HOST: "backend.sample"
BACKEND_PORT: "5001"
Vous êtes maintenant prêt à partir. Poussez à nouveau le conteneur de service apl sur ECR. Si db-service et mysql sont déjà en cours d'exécution, il est nécessaire de définir et de déployer sur le même espace de noms, alors supprimez le service une fois. Ensuite, dans le dossier qui contient chaque fichier docker-compose.yml, ajoutez des options d'espace de noms et déployez. Les noms de service sont respectivement backend et apl.
Déployer le service backend
$ ecs-cli compose --project-name backend service up --private-dns-namespace sample --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
Déployer le service APL
$ ecs-cli compose --project-name apl service up --private-dns-namespace sample.com --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
En guise d'avertissement, vous devez également ajouter --ecs-profile
à la commande deploy dans le didacticiel.
$ curl http://XXX.XXX.XXX.XXX:5002/results
{"addedStudents": [{"id": "1001", "name": "Alice", "score": 60, "isPassed": false}, {"id": "1002", "name": "Bob", "score": 80, "isPassed": true}]}
Enfin, la communication de apl-service a été confirmée. Voici un aperçu des voies de communication.
J'ai beaucoup corrigé, je vais donc résumer les commandes source et déployer qui ont changé. De plus, la région a été faite avec ap-nord-est-1, mais veuillez noter que ceux qui utilisent un autre doivent changer.
mysql/Dockerfile
FROM mysql/mysql-server:5.7
COPY ./db/mysql_init/setup.sql /docker-entrypoint-initdb.d/setup.sql
RUN chown -R mysql /var/lib/mysql && \
chgrp -R mysql /var/lib/mysql
setup.sql est omis car il est inchangé
db-service/Dockerfile
FROM python:3.6
#Travailler DIR sur le conteneur
WORKDIR /usr/src/
#Installation de la bibliothèque
RUN pip install flask mysql-connector-python
#Copiez la source dans le conteneur
COPY ./src/server.py /usr/src/server.py
COPY ./src/students.py /usr/src/students.py
CMD python ./server.py
db-service / src / server.py
n'a pas changé, il est donc omis.
db-service/src/students.py
import json
import mysql.connector as mydb
import os
def get_students():
conn = mydb.connect(user=os.environ['DB_USER'], passwd=os.environ['DB_PASS'],
host=os.environ['DB_HOST'], port=os.environ['DB_PORT'])
cur = conn.cursor()
sql_qry = 'select id, name, score from DB01.students;'
cur.execute(sql_qry)
rows = cur.fetchall()
results = [{"id": i[0], "name": i[1], "score": i[2]} for i in rows]
return_json = json.dumps({"students": results})
cur.close()
conn.close()
return return_json
apl-service/Dockerfile
FROM python:3.8
#Travailler DIR sur le conteneur
WORKDIR /usr/src/
#Installation de la bibliothèque
RUN pip install requests flask
#Copiez la source dans le conteneur
COPY ./src/server.py /usr/src/server.py
COPY ./src/results.py /usr/src/results.py
CMD python ./server.py
apl-service/src/server.py
from flask import Flask, request, abort, render_template, send_from_directory
from results import get_results
API_URI = 'http://db-service:5001/students'
app = Flask(__name__)
@app.route('/results', methods=['GET'])
def local_endpoint():
return get_results(API_URI)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5002)
ʻApl-service / src / results.py` n'a pas changé, donc il est omis.
docker-compose (db-service, mysql)
docker-compose.yml
# aws_account_id,la région doit être modifiée en fonction de la situation réelle
version: '3'
services:
mysql:
image: <aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/python-mysql/mysql
ports:
- "3306:3306"
environment:
MYSQL_USER: user
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: DB01
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --skip-character-set-client-handshake
logging:
driver: awslogs
options:
awslogs-group: python-docker
awslogs-region: ap-northeast-1
awslogs-stream-prefix: mysql
db-service:
image: <aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/python-mysql/db-service
ports:
- "5001:5001"
environment:
DB_HOST: 127.0.0.1
DB_USER: "user"
DB_PASS: "password"
DB_PORT: "3306"
logging:
driver: awslogs
options:
awslogs-group: python-docker
awslogs-region: ap-northeast-1
awslogs-stream-prefix: db-service
docker-compose (apl-service)
docker-compose.yml
# aws_account_id,la région doit être modifiée en fonction de la situation réelle
version: '3'
services:
apl-service:
image: <aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/python-mysql/apl-service
ports:
- "5002:5002"
environment:
BACKEND_HOST: "backend.sample"
BACKEND_PORT: "5001"
logging:
driver: awslogs
options:
awslogs-group: python-docker
awslogs-region: ap-northeast-1
awslogs-stream-prefix: apl-service
Nous couvrirons les principales parties de l'étape 3 du didacticiel (Création d'un cluster de tâches Fargate à l'aide de l'interface de ligne de commande Amazon ECS), mais celles avec des commentaires sont les modifications. Remplacez les parenthèses et les régions par des valeurs réelles.
Étape 3
$ ecs-cli up --cluster-config sample-config --ecs-profile sample-profile
$ aws ec2 describe-security-groups --filters Name=vpc-id,Values=<vpc-id> --region ap-northeast-1
$ aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 80 --cidr 0.0.0.0/0 --region ap-northeast-1
# 5001,5002 également autorisé
$ aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 5001 --cidr 0.0.0.0/0 --region ap-northeast-1
$ aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 5002 --cidr 0.0.0.0/0 --region ap-northeast-1
Étape 5
#Déployer le backend-Ajout d'une option pour créer un espace de noms pour la découverte de services
$ ecs-cli compose --project-name backend service up --create-log-groups --cluster-config sample-config --private-dns-namespace sample --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
#déployer apl-Identique au backend
$ ecs-cli compose --project-name apl service up --create-log-groups --cluster-config sample-config --private-dns-namespace sample --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
Étape 6
$ ecs-cli compose --project-name backend service up --private-dns-namespace sample --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
$ ecs-cli compose --project-name apl service up --private-dns-namespace sample.com --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
Étape 10
$ ecs-cli compose --project-name backend service down --cluster-config sample-config --ecs-profile sample-profile
$ ecs-cli compose --project-name apl service down --cluster-config sample-config --ecs-profile sample-profile
$ ecs-cli down --force --cluster-config sample-config --ecs-profile sample-profile
--Image de l'utilisation d'applications de micro-service sur Fargate --Mécanismes et méthodes de communication intra-tâche et de communication inter-tâche dans ECS
Je sens que j'ai approfondi ma compréhension non seulement de Fargate mais aussi des microservices en général parce que j'ai fait des microservices avec un scratch complet. J'ai beaucoup écrit sur ce à quoi j'étais accro, donc c'était peut-être un article difficile à comprendre. Cela fait longtemps, mais merci d'avoir lu. Je vous serais très reconnaissant si vous pouviez signaler ou poser des questions.
Recommended Posts