เริ่มต้นกับ Microservice #1

CrossKnight
5 min readMar 10, 2020

--

https://www.edureka.co/blog/microservice-architecture/

ความแตกต่างของ Microservices และ Monolithic นั้นค่อนข้างชัดเจน ข้อเสียของ Monolithic คือเมื่อต้องการซ่อมแซมส่วนใด ก็จะมีผลกระทบกับระบบซอฟต์แวร์ทั้งหมด เนื่องจากมีการผูกติดกันอย่างแน่นหนา (Tightly Coupled) หรือเมื่อ Technology ใหม่เข้ามา และเราต้องการใช้ Technology ใหม่ก็ทำได้ยากกว่า Microservices เนื่องจากต้องรื้อทั้งระบบ แม้กระทั่งการ Scale ก็ยังทำได้ยากเนื่องจากต้อง Scale ทั้งระบบหรือแทบจะกล่าวได้ว่าสามารถ Scale ได้เฉพาะ แบบกระจายแบบ Round-Robin ได้เท่านั้นไม่สามารถ Scale ในรูปแบบ Partitioning ได้

Microservices ดีกว่า Monolithic ยังไงบ้าง?

  • แก้ไขปัญหาความซับซ้อนของ Application
  • Developer มีความเป็นอิสระในการเลือก Technology
  • เพิ่ม Service ได้ง่าย
  • Deploy ได้รวดเร็ว
  • การ Scale ก็เป็นอิสระ
  • เปลี่ยน Technology ได้ง่าย

แต่ Microservices และ Monolithic นั้นจริงแล้วก็มีข้อดีเสียอยู่นะครับใช่ว่า Microservices จะดีเสียทุกอย่าง อย่างไร Monolithic ก็มีข้อดีเราจึงเลือกใช้ Monolithic และ Microservices ให้เหมาะกับงานเพื่อให้ได้ประโยชน์สูงสุดในองค์กรของท่านครับ

RabbitMQ

เป็นพระเอกของงานนี้เลย ทำหน้าที่เป็นตัวกลางคอยรับส่ง Event ให้กับ Service ที่มา Subscribe Event นั้นๆ หรือเรียกว่าการทำงานแบบ Event-driven

mq_dock/docker-compose.yml

version: "2"
services:
rabbitmq:
image: "rabbitmq:management"
container_name: rabbitmq
restart: always

networks:
default:
external:
name: microservice_network

และสั่งรันไว้ตามปกติด้วย

docker-compose up -d

ต่อไปเราจะมาสร้างตัว Event กัน

register_gateway_dock
├── docker-compose.yml
└── python
├── Dockerfile
├── api.py
└── requirements.txt

docker-compose.yml

version: "3"services:register:container_name: register_gatewaybuild: python/restart: alwaysports:- "7001:80"networks:default:external:name: microservice_network

Dockerfile

FROM python:3.7.3-alpine3.8
RUN apk add --no-cache mariadb-dev build-base
WORKDIR /app
COPY api.py .
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
CMD uvicorn api:app --host 0.0.0.0 --port 80

requirements.txt

fastapi
uvicorn
pydantic
nameko

api.py

from fastapi import FastAPI
from pydantic import BaseModel
from nameko.rpc import rpc
from nameko.standalone.rpc import ClusterRpcProxy

class Student(BaseModel):
firstname:str
lastname:str
email:str

app = FastAPI()

broker_cfg = {'AMQP_URI': "amqp://guest:guest@rabbitmq"}

@app.post("/register/")
def api(student_item: Student):
with ClusterRpcProxy(broker_cfg) as rpc:
sid =rpc.student.insert(student_item.firstname, student_item.lastname, student_item.email)
rpc.enroll.insert.call_async(sid, student_item.firstname, student_item.lastname)
rpc.email.send.call_async(sid, student_item.firstname, student_item.lastname, student_item.email)

print(sid)
return {'results': 'registered'}

Student Service

Service ตัวแรกมีหน้าที่คอยเก็บข้อมูลนักศึกษา จะถูกเรียกใช้งานจาก RPC

student_dock
|__ docker-compose.yml
|__ mariadb/
| |__ data/
| |__ initdb/
|__devops_db.sql
| |__ backup/
|__ python/
|__ Dockerfile
|__ requirements.txt
|__ rpc.py

docker-compose.yml

version: '3'

services:
student_rpc:
container_name: student_rpc
build: python/
restart: always

depends_on:
- db
networks:
- microservice
- default

db:
container_name: mariadb
image: mariadb:latest
restart: always
volumes:
- ./mariadb/initdb/:/docker-entrypoint-initdb.d
- ./mariadb/data/:/var/lib/mysql/
environment:
- MYSQL_ROOT_PASSWORD=devops101
- MYSQL_DATABASE=devops_db
- MYSQL_USER=devops
- MYSQL_PASSWORD=devops101
pma:
container_name: student-phpmyadmin
image: phpmyadmin/phpmyadmin
restart: always

networks:
- webproxy
- default

environment:
VIRTUAL_HOST: mydb.lab10.cpsudevops.com
LETSENCRYPT_HOST: mydb.lab10.cpsudevops.com

expose:
- "80"

networks:
default:
external:
name: student_network
microservice:
external:
name: microservice_network
webproxy:
external:
name: webproxy

Dockerfile

FROM python:3.7.3-alpine3.8
RUN apk add --no-cache mariadb-dev build-base
WORKDIR /app
COPY rpc.py .
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
CMD nameko run rpc --broker amqp://guest:guest@rabbitmq:5672

requirements.txt

mysqlclient
nameko

rpc.py

import MySQLdb
from nameko.rpc import rpc

def connect():
DBconnect = MySQLdb.connect(host='db',
user='devops',
passwd='devops101',
db='devops_db',
port=3306)
return DBconnect

def insert(firstname, lastname, email):
DBconnect = connect()
cur = DBconnect.cursor()
cur.execute("INSERT INTO student (FirstName, LaseName, Email) VALUES (%s, %s, %s);", (firstname, lastname, email))
id = cur.lastrowid
DBconnect.commit()
DBconnect.close()
return id

class Service:
name = "student"

@rpc
def insert(self, firstname, lastname, email):
result = insert(firstname, lastname, email)
return result

devops_db.sql

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";

CREATE TABLE `student` (
`id` int NOT NULL,
`FirstName` varchar(255) NOT NULL,
`LaseName` varchar(255) DEFAULT NULL,
`Email` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `student`
ADD PRIMARY KEY (`id`);

ALTER TABLE `student`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;

Enroll Service

เป็น Service เก็บข้อมูลการลงทะเบียน จะถูกเรียกใช้งานผ่าน RPC API จะเพิ่มรายวิชาเข้าไปยัง Database

enroll_dock
|__ docker-compose.yml
|__ mariadb/
| |__ data/
| |__ initdb/
|__devops_db.sql
| |__ backup/
|__ python/
|__ Dockerfile
|__ requirements.txt
|__ rpc.py

docker-compose.yml

version: '3'

services:
enroll_rpc:
container_name: enroll_rpc
build: python/
restart: always

depends_on:
- db
networks:
- microservice
- default

db:
container_name: enroll_mariadb
image: mariadb:latest
restart: always
volumes:
- ./mariadb/initdb/:/docker-entrypoint-initdb.d
- ./mariadb/data/:/var/lib/mysql/
environment:
- MYSQL_ROOT_PASSWORD=devops101
- MYSQL_DATABASE=devops_db
- MYSQL_USER=devops
- MYSQL_PASSWORD=devops101
pma:
container_name: enroll-phpmyadmin
image: phpmyadmin/phpmyadmin
restart: always

networks:
- webproxy
- default

environment:
VIRTUAL_HOST: mydb2.lab10.cpsudevops.com
LETSENCRYPT_HOST: mydb2.lab10.cpsudevops.com

expose:
- "80"

networks:
default:
external:
name: enroll_network
microservice:
external:
name: microservice_network
webproxy:
external:
name: webproxy

Dockerfile

FROM python:3.7.3-alpine3.8
RUN apk add --no-cache mariadb-dev build-base
WORKDIR /app
COPY rpc.py .
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
CMD nameko run rpc --broker amqp://guest:guest@rabbitmq:5672

requirements.txt

mysqlclient
nameko

rpc.py

import MySQLdb
from nameko.rpc import rpc

def connect():
DBconnect = MySQLdb.connect(host='db',
user='devops',
passwd='devops101',
db='devops_db',
port=3306)
return DBconnect

def insert(id, firstname, lastname, subjectid, term, year):
DBconnect = connect()
cur = DBconnect.cursor()
cur.execute("INSERT INTO enroll (id, name, subjectid, term, year) VALUES (%s, %s, %s, %s, %s);", (id, firstname + ' ' + lastname, subjectid, term, year))
id = cur.lastrowid
DBconnect.commit()
DBconnect.close()
return id

class Service:
name = "enroll"

@rpc
def insert(self, id, firstname, lastname):
result = insert(id, firstname, lastname, '081102', 1, 2563)
result = insert(id, firstname, lastname, '520101', 1, 2563)
result = insert(id, firstname, lastname, '511100', 1, 2563)
result = insert(id, firstname, lastname, '517121', 1, 2563)
return result

devops_db.sql

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";

CREATE TABLE `enroll` (
`id` int NOT NULL,
`name` varchar(255) NOT NULL,
`subjectid` varchar(255) NOT NULL,
`term` int NOT NULL,
`year` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `enroll`
ADD PRIMARY KEY (`id`, `subjectid`, `term`, `year`);

COMMIT;

Email Service

และมาถึง Service มีหน้าที่ส่งข้อความยืนยันการลงทะเบียนไปยัง E-mail ผู้สมัคร ซึ่งก็จะถูกเรียกใช้งานผ่านการเรียก RPC อีกเช่นเดิม

email_dock
|__ docker-compose.yml
|__ python/
|__ Dockerfile
|__ requirements.txt
|__ rpc.py

docker-compose.yml

version: '3'

services:
email_rpc:
container_name: email_rpc
build: python/
restart: always

networks:
- microservice
- default

smtp:
container_name: email_smtp
image: bytemark/smtp
restart: always
environment:
RELAY_HOST: smtp.live.com
RELAY_PORT: 587
RELAY_USERNAME: yourEmail@hotmail.com
RELAY_PASSWORD: yourPassword

networks:
default:
external:
name: email_network
microservice:
external:
name: microservice_network

ในส่วนนี้แก้ไขตามผู้ให้บริการ email ของท่าน จากตัวอย่างคือของ Hotmail

Dockerfile

FROM python:3.7.3-alpine3.8
RUN apk add --no-cache mariadb-dev build-base
WORKDIR /app
COPY rpc.py .
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
CMD nameko run rpc --broker amqp://guest:guest@rabbitmq:5672

requirements.txt

nameko

rpc.py

import smtplib
from email.message import EmailMessage
from nameko.rpc import rpc

def send(id, firstname, lastname, email):
msg = EmailMessage()
text = "สวัสดีครับ คุณ " + firstname + " " + lastname + " รหัสนักศึกษา " + str(id) + " ได้ลงทะเบียนแล้ว"
msg.set_content(text)

msg['Subject'] = 'Register complete'
msg['To'] = email

s = smtplib.SMTP("smtp",25)
s.ehlo()
s.sendmail(from_addr = 'yourEmail@hotmail.com', to_addrs = email, msg = msg.as_string())
s.quit()


class Email:
name = "email"

@rpc
def send(self, id, firstname, lastname, email):
send(id, firstname, lastname, email)

Note: ไม่ได้เขียนในส่วนให้ service แต่ละตัวเริ่มต้นการทำงาน อย่าลืมสั่งด้วย docker-compose up -d

หลังจากนั้นก็ทดลองเล่นได้ผ่าน FastAPI ที่ domainname:7001/docs

http://lab10.cpsudevops.com:7001/docs
http://lab10.cpsudevops.com:7001/docs
https://mydb.lab10.cpsudevops.com/
https://mydb2.lab10.cpsudevops.com/
Email ที่ถูกส่งเข้ามา

Reference

https://www.softnix.co.th/2018/08/09/microservices-in-10-minutes/
https://engineering.thinknet.co.th/microservices-communication-5cdde1b47273
https://blog.pjjop.org/build-microservice-on-docker-container/

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response