Dies ist ein Artikel über das Erstellen einer Microservice-ähnlichen App mit Python, MySQL und Docker und deren Bereitstellung in Fargate.
Ich wollte Fargate verwenden, das (teilweise) das heißeste in AWS sein soll, und ich interessierte mich für Microservice-Architektur. Da ich mich jedoch nie richtig mit Mikrodiensten befasst habe, ist es eine Wahnarchitektur, die "ich frage mich, ob es so aussieht". Wenn Sie Fehler haben, würden wir uns freuen, wenn Sie uns Ihre Meinung mitteilen könnten.
Darüber hinaus verfügt AWS über RDS. Da der Container jedoch in erster Linie auch Anfänger ist, verwende ich den MySQL-Container auch zum Lernen.
Es gibt viele Erklärungen im Internet, aber ich werde mein Verständnis kurz beschreiben.
Fargate
Das ist das Verständnis. Mit anderen Worten, dieses Mal werde ich versuchen, den mit __Docker erstellten Mikrodienst für Fargate bereitzustellen, den Verwaltungsdienst von AWS __.
Stellen Sie den Microservice zuerst lokal als Host bereit und bringen Sie ihn dann zu Fargate. Nach einer erfolgreichen Bereitstellung bei Fargate besteht das Ziel darin, mit den beiden Mikrodiensten von außen über HTTP kommunizieren zu können.
Stellen Sie die App auf Ihrem lokalen Mac bereit. Die Quellen usw. sind wie folgt.
Erstellen Sie Ordner für jeden der drei Container (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 und die tatsächlich registrierten Daten
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
#DIR am Behälter arbeiten
WORKDIR /usr/src/
#Bibliotheksinstallation
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
apl-service/Dockerfile
FROM python:3.8
#DIR am Behälter arbeiten
WORKDIR /usr/src/
#Bibliotheksinstallation
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
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/
Ich werde das Ergebnis weglassen, es aber mit dem Befehl docker-compose bereitstellen.
build&up
$ docker-compose build
$ docker-compose up
Versuchen Sie nach erfolgreicher Bereitstellung, als Bestätigung der Kommunikation über HTTP auf jeden Mikrodienst zuzugreifen. Wie Sie der Quelle entnehmen können, verwendet db-service die Ports 5001 und apl-service die Ports 5002.
db-Zugangsservice
$ curl http://127.0.0.1:5001/students
{"students": [{"id": "1001", "name": "Alice", "score": 60}, {"id": "1002", "name": "Bob", "score": 80}]}
apl-Zugangsservice
$ 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}]}
Sie haben den zugrunde liegenden (wahnhaften) Mikroservice jetzt erfolgreich lokal bereitgestellt. Da das Ergebnis der apl-service-API zurückgegeben wird, können Sie auch bestätigen, dass eine Kommunikation zwischen dem apl-service und den db-service microservices besteht.
Ich werde das sofort zu Fargate bringen! Aber es war eine lange Zeit von hier ...
Es ist ein Konfigurationsdiagramm der endgültigen Version, das auf der Bereitstellung in Fargate basiert.
Die Punkte sind wie folgt.
―― 1 Bereitstellen von Microservices auf einem Fargate.
Basierend auf diesen werden wir bereitstellen.
Schieben Sie zunächst die erstellten drei Docker-Images (apl-service, db-service, mysql) an ECR. Die Benutzeroberfläche von ECR ist leicht zu verstehen. Wenn Sie also in der AWS-Konsole auf "Repository erstellen" klicken und so fortfahren, wie sie ist, können Sie sie pushen.
docker rm
aus.Mit ECS können Sie docker-compose.yml verwenden. Nachdem Sie ecs-cli festgelegt haben, werden wir es gemäß dem folgenden Tutorial für Fargate bereitstellen. Es ist jedoch erforderlich, docker-compose.yml usw. zu ändern. Ich werde verschiedene Korrekturpunkte ansprechen, aber die Quelle der endgültigen Version finden Sie am Ende des Artikels. Wenn Sie also beschäftigt sind, lesen Sie bitte dies.
Lernprogramm: Erstellen eines Clusters von Fargate-Aufgaben mithilfe der Amazon ECS-CLI
Außerdem verwenden wir in dieser App die Ports 5001 und 5002, sodass wir diese in den Sicherheitsgruppeneinstellungen in Schritt 3 zulassen müssen.
Wenn Sie 5001 zulassen
$ aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 5001 --cidr 0.0.0.0/0 --region <region>
Beispiel) Ordner "aws_In "Arbeit" speichern und mit dem Backend des Aufgabennamens bereitstellen
$ pwd
/Users/<Kürzung>/aws_work
$ ls docker-compose.yml # db-service +MySQL Docker-compose.yml
docker-compose.yml
$ ecs-cli compose --project-name backend service up ~
Beispiel) Ordner "aws_In "Arbeit" speichern und mit dem Aufgabennamen apl bereitstellen
$ pwd
/Users/<Kürzung>/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
, hostname
werden nicht benötigt, geben Sie stattdessen image
an.image
.Beispiel) Docker-compose.yml Korrekturpunkte(Bildeinstellungen)
mysql:
image: <aws_account_id>.dkr.ecr.<region>.amazonaws.com/python-mysql/mysql
--Fargate unterstützt keine dauerhaften Speichervolumes. Klingt schwierig, bedeutet aber, dass Sie in docker-compose.yml keine Volumes verwenden können. Daher wird die Quelle wie server.py nicht im Container wiedergegeben. --So kommentieren (oder löschen) Sie Volumes aus docker-compose.yml und ändern Sie sie so, dass sie COPY in die Docker-Datei aufnehmen. --db-service und apl-service nehmen ähnliche Änderungen vor.
Beispiel) Docker-compose.yml Korrekturpunkte
# volumes:
# - ./db-service/src/:/usr/src/
Beispiel) db-service/Dockerfile-Änderungspunkte
#Kopieren Sie die Quelle in den Container
COPY ./src/server.py /usr/src/server.py
COPY ./src/students.py /usr/src/students.py
Beispiel) Docker-compose.yml Korrekturpunkte(Containerprotokoll)
mysql:
logging:
driver: awslogs
options:
awslogs-group: python-docker
awslogs-region: <region>
awslogs-stream-prefix: mysql
Beachten Sie auch, dass Fargate, obwohl diesmal nicht behoben, "Ports" auf Host- und Containerseite dieselbe Nummer haben muss. Sie können beispielsweise "80: 5001" lokal angeben, aber es wird eine Fehlermeldung angezeigt, wenn Sie in Fargate nicht "5001: 5001" angeben.
Ich habe ungefähr 4 Punkte behoben, aber es funktioniert immer noch nicht.
Von hier aus ist es die Modifikation, die erforderlich ist, um die oben erwähnte Kommunikation zwischen Containern innerhalb der Aufgabe und der Kommunikation zwischen Aufgaben zu realisieren. Um die Kommunikation zwischen Containern zu realisieren, werden wir zunächst db-service und mysql beschreiben.
Wenn Sie nach der Bereitstellung des Fixes den Status mit dem Befehl "ecs-cli compose ~ service ps ~" überprüfen, sollte er "RUNNING" sein. Eigentlich ist es das Ergebnis von ps nach dem Start.
$ 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
Da die IP-Adresse von Globus angezeigt wird, versuche ich, mit dem Datenbankdienst von Mac über GET zu kommunizieren, aber wie oben erwähnt, funktioniert dies immer noch nicht.
$ 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>
Da es sich auf der ECS-Konsole befindet, wird beim Überprüfen des Protokolls auf der Registerkarte Protokolle usw. die Fehlermeldung angezeigt, dass der Name nicht aufgelöst werden kann. In diesem Fall erhalte ich die Fehlermeldung, dass der Hostname von mysql nicht vom Datenbankdienst gefunden werden kann. Der Punkt ist, dass es keine Kommunikation zwischen den Containern gibt. Ich habe es bei der lokalen Bereitstellung geschrieben, aber dieses Mal verwende ich keine Links. Außerdem unterstützt der Fargate-Typ Links überhaupt nicht.
Die Lösung besteht darin, dass auf die Kommunikation zwischen den Containern von Fargate über die Portnummer zugegriffen werden kann, wenn dieselbe Aufgabe ausgeführt wird. Ändern Sie also die Quelle von students.py wie folgt. Da die On-Codierung des Hostnamens subtil ist, werde ich ihn so ändern, dass er als Umgebungsvariable aus docker-compose.yml übergeben wird.
db-service/src/students.py Fixpunkte
import json
import mysql.connector as mydb
import os #hinzufügen
def get_students():
#Behoben, um von Umgebungsvariablen zu erhalten
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.yml Korrekturpunkte(db-service)
db-service:
-Kürzung-
environment:
DB_HOST: 127.0.0.1
DB_USER: "user"
DB_PASS: "password"
DB_PORT: "3306"
Drücken Sie erneut auf ECR und stellen Sie es bereit.
Wenn Sie Fargate einmal bereitgestellt haben, um die Änderungen widerzuspiegeln, löschen Sie es einmal mit dem Befehl `ecs-cli compose ~ service down ~`
im Lernprogramm und `ecs-cli compose Starten Sie mit dem Befehl ~ service up ~
`neu.
Sie können bestätigen, dass die HTTP-Kommunikation vom Mac zum Datenbankdienst erfolgreich ist. Das Folgende ist eine Überprüfung der Kommunikationspfade.
$ curl http://XXX.XXX.XXX.XXX:5001/students
{"students": [{"id": "1001", "name": "Alice", "score": 60}, {"id": "1002", "name": "Bob", "score": 80}]}
Lassen Sie als nächstes den apl-Service kommunizieren. Wie bereits erwähnt, führt apl-service die Datenbankdienst-API aus, sodass es mit einer anderen Aufgabe kommunizieren muss. Um dies mit Fargate zu erreichen, aktivieren Sie die Diensterkennung und den Start. Der Mechanismus der Diensterkennung wird weggelassen, da der folgende Artikel von Classmethod sehr einfach zu verstehen ist. Kurz gesagt, zwischen Microservices wird automatisch eine Aufzeichnung von Route53 erstellt, und die Namenserkennung ist möglich.
Da wir dieses Mal ecs-cli verwenden, werden wir es mit Befehlen bereitstellen, indem wir uns auf das folgende offizielle AWS-Tutorial beziehen.
Der Hostname, wenn der Dienst erkannt wird, lautet "service_name.namespace". Dieses Mal wird der Dienstname als Backend und der Namespace als Beispiel erstellt. Um von apl-service auf den Datenbankdienst zuzugreifen, muss der Hostname "backend.sample" festgelegt werden. Ändern Sie es also wie bei db-service, um den Hostnamen aus der Umgebungsvariablen festzulegen.
apl-service/src/server.py Fixpunkte
from flask import Flask, request, abort, render_template, send_from_directory
from results import get_results
import os #hinzufügen
#Behoben, um von Umgebungsvariablen zu erhalten
API_URI = 'http://' + os.environ['BACKEND_HOST'] +':' + os.environ['BACKEND_PORT'] + '/students'
docker-compose.yml Korrekturpunkte(apl-service)
apl-service:
-Kürzung-
environment:
BACKEND_HOST: "backend.sample"
BACKEND_PORT: "5001"
Jetzt können Sie loslegen. Schieben Sie den apl-Service-Container erneut auf ECR. Wenn db-service und mysql bereits ausgeführt werden, muss derselbe Namespace festgelegt und bereitgestellt werden. Löschen Sie den Dienst daher einmal. Fügen Sie dann in dem Ordner, der jede Datei docker-compose.yml enthält, Namespace-Optionen hinzu und stellen Sie sie bereit. Die Dienstnamen sind Backend bzw. Apl.
Stellen Sie den Backend-Service bereit
$ ecs-cli compose --project-name backend service up --private-dns-namespace sample --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
Stellen Sie den apl-Dienst bereit
$ ecs-cli compose --project-name apl service up --private-dns-namespace sample.com --vpc <vpc-id> --enable-service-discovery --ecs-profile sample-profile
Als Einschränkung sollten Sie dem Befehl "deploy" im Lernprogramm auch "--ecs-profile" hinzufügen.
$ 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}]}
Schließlich wurde die Kommunikation von apl-service bestätigt. Das Folgende ist eine Überprüfung der Kommunikationspfade.
Ich habe viel behoben, daher fasse ich die geänderten Quell- und Bereitstellungsbefehle zusammen. Außerdem wurde die Region mit ap-nordost-1 erstellt. Bitte beachten Sie jedoch, dass diejenigen, die eine andere verwenden, Änderungen vornehmen müssen.
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
Da setup.sql unverändert bleibt, wird es weggelassen.
db-service/Dockerfile
FROM python:3.6
#DIR am Behälter arbeiten
WORKDIR /usr/src/
#Bibliotheksinstallation
RUN pip install flask mysql-connector-python
#Kopieren Sie die Quelle in den Container
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
hat sich nicht geändert, daher wird es weggelassen.
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
#DIR am Behälter arbeiten
WORKDIR /usr/src/
#Bibliotheksinstallation
RUN pip install requests flask
#Kopieren Sie die Quelle in den Container
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
hat sich nicht geändert, daher wird es weggelassen.
docker-compose (db-service, mysql)
docker-compose.yml
# aws_account_id,Region muss entsprechend der tatsächlichen Situation geändert werden
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,Region muss entsprechend der tatsächlichen Situation geändert werden
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
Ich werde die Hauptteile aus Schritt 3 des Tutorials (Erstellen eines Clusters von Fargate-Aufgaben mit der Amazon ECS-CLI) behandeln, aber diejenigen mit Kommentaren sind die Änderungen. Ersetzen Sie Klammern und Regionen durch tatsächliche Werte.
Schritt 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 auch erlaubt
$ 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
Schritt 5
#Backend bereitstellen-Option zum Erstellen eines Namespace für die Serviceerkennung hinzugefügt
$ 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
#apl bereitstellen-Gleich wie 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
Schritt 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
Schritt 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
Ich habe das Gefühl, dass ich mein Verständnis nicht nur für Fargate, sondern auch für Microservices im Allgemeinen vertieft habe, weil ich Microservices mit vollem Kratzer erstellt habe. Ich habe viel darüber geschrieben, wovon ich süchtig war, daher war es möglicherweise schwierig, diesen Artikel zu verstehen. Es ist lange her, aber danke fürs Lesen. Ich wäre Ihnen sehr dankbar, wenn Sie darauf hinweisen oder Fragen stellen könnten.
Recommended Posts