การพัฒนา Microservice บน Docker Container สำหรับผู้เริ่มต้น

บทความโดย ผศ.ดร.ณัฐโชติ พรหมฤทธิ์
ภาควิชาคอมพิวเตอร์
คณะวิทยาศาสตร์
มหาวิทยาลัยศิลปากร

บทความนี้เราจะทำความเข้าใจแนวคิดของ Microservice ซึ่งเป็นสถาปัตยกรรมการพัฒนา Software ด้วยการแตกระบบออกเป็นส่วนย่อยที่มีหน้าที่การทำงานเพียงเรื่องเดียว ซึ่งในปัจจุบันบริษัทชั้นนำหลายบริษัทได้นำสถาปัตยกรรมแบบนี้ไปใช้งานจริงๆ เป็น Production เช่น Amazon, Netflix และ Wongnai ฯลฯ

โดยตั้งแต่ตอนที่ 3 ของบทความ เราจะได้ทดลองสร้าง Microservice เพื่อจำลองงานทะเบียนนักศึกษา ที่มีการแลกเปลี่ยนข้อมูลแบบ RESTful API และ RPC (Remote Procedure Call) ซึ่งจะมีการ Deploy บน Docker Container ครับ

หมายเหตุ

docker-compose down --rmi all

1.Monolithic

ภาพจาก https://medium.com/@velusamyvenkatraman/lets-begin-with-3-tire-architecture-39c1b3f9fa51

หลายคนน่าจะรู้จักสถาปัตยกรรมแบบ Three-tier Application Architecture ซึ่งมีการแบ่งชิ้นส่วน (Component) ของ Software เป็น 3 Layer ได้แก่ Presentation Layer, Application Layer และ Data Layer

การพัฒนาซอฟต์แวร์ภายใต้โครงสร้างของสถาปัตยกรรมแบบนี้ บริษัทมักแบ่งการทำงานของ Layer ตามความถนัดของแผนกหรือทีม ซึ่งแต่ละทีมจะมีความเป็น Specialists สูงในงานเฉพาะด้าน โดยซอฟต์แวร์ที่พัฒนาจะแบ่งออกเป็น Module ที่ใช้ภาษาเดียวกัน ฐานข้อมูลเดียวกัน และรันใน Process เดียวกัน สถาปัตยกรรมของ Software แบบนี้เรียกว่า Monolithic

ภาพจาก https://martinfowler.com/articles/microservices.html

สมมติว่าบริษัทแห่งหนึ่งมีนักพัฒนา 10 คน แบ่งทีมพัฒนา Software "ระบบทะเบียนนักศึกษา" เป็น 2 ทีม ทีมละ 5 คน ทีมที่ 1 รับผิดชอบ Student Module ทีมที่ 2 รับผิดชอบ Enroll Module ในการพัฒนาแบบ Monolithic ทั้งสองทีมจะต้องเลือกใช้เทคโนโลยีเดียวกัน และ Deploy Software ที่รวมทุก Function ไว้ใน Process เดียว ดังภาพด้านล่างซ้าย การปรับขนาดเพื่อรองรับ Load ของ Monolithic จึงต้อง Replicate ไปทั้งก้อน

ภาพจาก https://martinfowler.com/articles/microservices.html

แต่หากมี Module หนึ่งพัง อาจส่งผลกระทบกับการทำงานในส่วนอื่นของระบบ ยิ่งการ Deploy เป็นก้อนใหญ่มาก ความเสี่ยงต่อระบบโดยรวมก็จะยิ่งมากขึ้น

2.Microservices

เมื่อเราแตกระบบออกเป็นส่วนย่อยที่มีหน้าที่การทำงานเพียงเรื่องเดียวอย่างชัดเจน ดังภาพล่างขวามือ หากมีบาง Service พัง มันจะกระทบกับ Service อื่นน้อยกว่า

ภาพจาก https://martinfowler.com/articles/microservices.html

นอกจากนี้แต่ละทีมจะสามารถทำงานเสร็จโดยไม่ต้องรอทีมอื่นทำงานบางอย่างก่อน ทำให้แต่ละทีมสามารถพัฒนา ทดสอบ และ Deploy Software ได้อย่างอิสระ โดยเลือกใช้เทคโนโลยีที่แตกต่างกัน (รวมทั้ง Database) ตามความเหมาะสม

ภาพจาก https://martinfowler.com/articles/microservices.html

โครงสร้างของสถาปัตยกรรมแบบนี้มีผลต่อทีมพัฒนา ซึ่งสมาชิกในทีมบางคนจะต้องเป็นคนเขียน Code ในส่วนของ Font-End, Back-End และ Database รวมทั้งทำ Unit Test, Code Review, Deploy และคอยดู Alert จากระบบ แล้วรีบแก้ไขปัญหา ถ้าพบ Bug ขณะ Deploy อาจต้อง  Rollback กลับไปยัง Version ก่อนหน้า

ลักษณะของทีมแบบนี้จะต้องเป็นทีมขนาดเล็ก แต่มีจำนวนคนเพียงพอที่จะช่วยกันดูแล Service ที่ตัวเองสร้างขึ้นมา ("Operating What You Build")

เมื่อ Service มีขนาดเล็ก เราจึงสามารถ Release Software ได้ถี่ขึ้น เป็นวันละหลายสิบครั้ง จากเดิมที่อาจมีการ Release ทุก 3 เดือน 6 เดือน ซึ่งต้องใช้กระบวนการรวบรวม และ Deploy Software แบบอัตโนมัติ (CI/CD)

การแบ่งงานเป็น Service ขนาดเล็ก ทำให้การปรับขนาดเพื่อรองรับ Load ทำได้ง่ายขึ้น เพียงเลือกขยายเฉพาะ Service ที่ถูกใช้งานเยอะๆ

ส่วนย่อยๆ ของ Software แบบนี้เรียกว่า Microservice ซึ่งเป็นแนวคิดที่ได้รับความนิยมเพิ่มขึ้นในปัจจุบัน โดยกระบวนการทาง DevOps ที่อาศัยเทคโนโลยีของ Container จะเป็นเครื่องมืออันทรงพลังที่จะทำให้ Microservice ประสบความสำเร็จครับ

อย่างไรก็ตามสิ่งที่ยากที่สุดของการพัฒนา Software แบบ Microservice Architecture คือ การออกแบบ เพราะถ้าเราออกแบบไม่ดีก็จะเพิ่มความซับซ้อนให้กับระบบโดยไม่จำเป็น เช่นกรณีที่เราแตกระบบออกเป็นส่วนย่อยจนกระทั่งเป็น Function แล้วแยกกัน Deploy บน Container เมื่อรวมเป็นระบบแล้วอาจจะทำให้เกิดการเรียกใช้ Service จำนวนมาก จนทำให้ระบบทำงานช้า และสิ้นเปลือง Network Bandwidth

อีกประเด็นที่ต้องระมัดระวัง คือ การเรียก Service ข้าม Bounded Context หรือข้ามขอบเขตของ Domain ซึ่งจะเกิดขึ้นเมื่อเรายึดหลักการ Don’t Repeat Yourself (DRY) โดยการแตกระบบออกเป็นส่วนย่อยที่ทำหน้าที่เฉพาะทางเพื่อให้เกิดการ Reuse มากจนเกินไป

ดังนั้นในการออกแบบ Microservice ที่ดี เราจะยึดหลัก 2 ประการ คือ

Loosely Coupling แต่ละ Service มีหน้าที่ทำงานเพียงอย่างเดียว และควรขึ้นกับ Service อื่นให้น้อยที่สุด

High Cohesion ภายในแต่ละ Service จะมีงานที่ร่วมกันทำตามเป้าหมาย

เพื่อให้เห็นภาพมากขึ้น เราจะยกตัวอย่าง Microservice จำลองระบบงานทะเบียนนักศึกษาที่มี Service ทั้งหมด 3 Service ได้แก่

  • Student เป็น Service ที่เก็บข้อมูลนักศึกษา
  • Enroll เป็น Service ที่เก็บข้อมูลการลงทะเบียน โดยสมมติว่าเป็นการลงทะเบียนแบบเหมาจ่าย
  • Email เป็น Service ที่ส่ง Email ไปยังนักศึกษาเพื่อแจ้งว่าได้ลงทะเบียนแล้ว

เมื่อนักศึกษาลงทะเบียน จะมี Flow เกิดขึ้น ดังนี้

  • สร้าง Record ใหม่ ที่ Student Service
  • สร้าง Record ลงทะเบียนใหม่ ที่ Enroll Service
  • ส่ง Email ไปยังนักศึกษาว่าได้ลงทะเบียนเรียบร้อยแล้ว

เราอาจออกแบบให้ Student Service เรียกใช้ Enroll และ Email Service ผ่าน RESTful API ซึ่งเมื่อมี Request เข้ามา มันจะต้องรอ Response จาก Enroll และ Email Service ก่อนที่ Student จะ Reply กลับไปยังผู้ใช้งาน โดยเราเรียกการสื่อสารแบบ Request/Reply แบบนี้ว่า Synchronous Communication

การออกแบบสถาปัตยกรรม Software แบบนี้จะทำให้เกิด High Coupling เพราะว่า Service ของเราผูกกันแน่น โดย Student Service จะทำงานเกินหน้าที่ของตัวเอง

จากเดิมที่ Student Service จะต้องทำงานหลายหน้าที่ เราจะสร้าง register_gateway เพื่อควบคุม Flow ของงานแทน โดยเรายอมให้เกิด High Coupling ที่ register_gateway นอกจากนี้เราจะใช้ RPC ในการสื่อสารกับ Student Service แบบ Synchronous (ต้องรอ  Student ID  จาก Student Service) แทนการใช้ RESTful API ขณะที่เราจะใช้ RPC ในการสื่อสารกับ Enroll Service และ Email Service แบบ Asynchronous เพราะสามารถทำทั้ง 2 งานขนานกันไปได้ โดย register_gateway ไม่จำเป็นต้องรอให้ Service ทั้ง 2 ตัว ทำงานเสร็จก่อนจึงจะ Reply กลับไปยังผู้ใช้

3.RabbitMQ

ก่อนจะสร้าง Register Gateway เพื่อเป็นหน้าด่านรับ Request และประสานงานกับ Service ต่างๆ เราจะต้องติดตั้ง RabbitMQ ให้ทำหน้าที่เป็นท่อส่งข้อมูล เมื่อ Service ต่างๆ สื่อสารกันผ่าน RPC โดยจะมีการ Config ตามขั้นตอนดังต่อไปนี้

  • Remote Login ไปยัง Cloud Server โดยใช้ ssh
ssh nc-user@labxx.cpsudevops.com
  • สร้าง Project ชื่อ mq_dock ภายใน Folder มีไฟล์ docker-compose.yml
.
|__ docker-compose.yml
  • แก้ไข docker-compose.yml เพื่อติดตั้ง RabbitMQ Container ตามตัวอย่างด้านล่าง
version: "2"
services:
  rabbitmq:
    image: "rabbitmq:management"
    container_name: rabbitmq
    restart: always
      
networks:
  default:
    external:
      name: microservice_network
  • สร้าง Bridge network โดยตั้งชื่อเป็น microservice_network
docker network create microservice_network
  • รัน Container ด้วย docker-compose และกลับไปที่ Terminal โดยใช้ parameter -d
docker-compose up -d
  • ดู Container ที่กำลังรันทั้งหมดที่ docker-compose.yml ดูแล
docker-compose ps

4.Register Gateway

ทีนี้เราจะสร้าง Register Gateway เพื่อประสานงานกับ Service ต่างๆ รวมทั้งการรับ Request จากผู้ใช้ผ่าน RESTful API ตามขั้นตอนดังนี้

  • สร้าง Project ใหม่ ภายใน Folder register_gateway_dock ประกอบด้วย ไฟล์ และ Folder ดังต่อไปนี้
.
|__ docker-compose.yml
|__ python/
   |__ Dockerfile
   |__ requirements.txt
   |__ api.py
  • แก้ไข docker-compose.yml ตามตัวอย่างด้านล่าง
version: '3'

services:
  register:
    container_name: register_gateway
    build: python/
    restart: always
    ports:
      - "7001:80"  
networks:
  default:
    external:
      name: microservice_network
  • แก้ไข Dockerfile ตามตัวอย่างด้านล่าง
FROM python:3.7.3-alpine3.8
RUN apk add --no-cache build-base
WORKDIR /app
COPY api.py .
COPY requirements.txt .
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt
CMD uvicorn api:app --host 0.0.0.0 --port 80
  • แก้ไขไฟล์ requirements.txt เพื่อติดตั้ง Libray ที่จำเป็น

เราจะใช้ fastapi และ uvicorn สำหรับสร้าง RESTful API โดยนิยามข้อมูลที่รับเข้าแบบ JSON ด้วย pydantic และใช้ nameko เพื่อเรียก Service ต่างๆ ด้วย RPC

fastapi
uvicorn
pydantic
nameko==2.10.0
  • แก้ไขไฟล์ 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'}
  • รัน Container ด้วย docker-compose และกลับไปที่ Terminal โดยใช้ parameter -d
docker-compose up -d
  • ดู Container ที่กำลังรันทั้งหมดที่ docker-compose.yml ดูแล
docker-compose ps

5.Student Service

Student เป็น Service ที่เก็บข้อมูลนักศึกษา เมื่อถูกเรียกใช้ผ่าน RPC มันจะสร้าง Record ใหม่ ในฐานข้อมูลของตัวเอง โดยเราจะ Config docker-compose และไฟล์อื่นๆ ตามขั้นตอนต่อไปนี้

  • สร้าง Project ใหม่ ภายใน Folder student_dock ซึ่งประกอบด้วย ไฟล์และ Folder ดังต่อไปนี้
.
|__ 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.lab20.cpsudevops.com
      LETSENCRYPT_HOST: mydb.lab20.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==2.10.0
  • แก้ไขไฟล์ 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 CHARSET=utf8 COLLATE utf8_general_ci;

ALTER TABLE `student`
  ADD PRIMARY KEY (`id`);
  
ALTER TABLE `student`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
  • สร้าง Bridge network โดยตั้งชื่อเป็น student_network
docker network create student_network
  • รัน Container ด้วย docker-compose และกลับไปที่ Terminal โดยใช้ parameter -d
docker-compose up -d
  • ดู Container ที่กำลังรันทั้งหมดที่ docker-compose.yml ดูแล
docker-compose ps

6.Enroll Service

Enroll เป็น Service ที่เก็บข้อมูลการลงทะเบียน เมื่อถูกเรียกใช้งานผ่าน RPC มันจะสร้าง Record ใหม่ ที่ประกอบด้วยวิชาต่างๆ ใน 1 ภาคการศึกษา ในฐานข้อมูลของตัวเอง โดยเราจะ Config docker-compose และไฟล์อื่นๆ ตามขั้นตอนต่อไปนี้

  • สร้าง Project ใหม่ ภายใน Folder enroll_dock ประกอบด้วย ไฟล์ และ Folder ดังต่อไปนี้
.
|__ 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.lab20.cpsudevops.com
      LETSENCRYPT_HOST: mydb2.lab20.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==2.10.0
  • แก้ไขไฟล์ 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 CHARSET=utf8 COLLATE utf8_general_ci;

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

COMMIT;
  • สร้าง Bridge network โดยตั้งชื่อเป็น enroll_network
docker network create enroll_network
  • รัน Container ด้วย docker-compose และกลับไปที่ Terminal โดยใช้ parameter -d
docker-compose up -d
  • ดู Container ที่กำลังรันทั้งหมดที่ docker-compose.yml ดูแล
docker-compose ps

7.Email Service

งานของ Email Service นั้นง่ายมากครับ เมื่อมีการลงทะเบียนแล้ว Email Service จะส่งข้อความยืนยันการลงทะเบียนกลับทาง Email โดยเราจะ Config docker-compose และไฟล์อื่นๆ ตามขั้นตอนต่อไปนี้

  • แต่ก่อนที่จะติดต่อกับ Relay Host สำหรับการรับส่ง Email โดยใช้ Email Account เช่น ของ Google เราจะต้องมีการลงชื่อเข้าใช้ด้วยรหัสผ่านสำหรับแอป (app password) ตามคำแนะนำต่อไปนี้ >> การลงชื่อเข้าใช้ด้วยรหัสผ่านสำหรับแอป
  • สร้าง Project ใหม่ ภายใน Folder email_dock ประกอบด้วย ไฟล์ และ Folder ดังต่อไปนี้
.
|__ docker-compose.yml
|__ python/
   |__ Dockerfile
   |__ requirements.txt
   |__ rpc.py
  • แก้ไข docker-compose.yml โดยเปลี่ยน RELAY_HOST, RELAY_USERNAME และ RELAY_PASSWORD ด้วยข้อมูล Remote SMTP Server ของตัวเอง
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.gmail.com
      RELAY_PORT: 587
      RELAY_USERNAME: perceptiondesignthailand@gmail.com
      RELAY_PASSWORD: xxx //Password จากการลงชื่อเข้าใช้ด้วย google app password

networks:
  default:
    external:
      name: email_network
  microservice:
    external:
      name: microservice_network
  • แก้ไข Dockerfile ตามตัวอย่าง
FROM python:3.7.3-alpine3.8
RUN apk add --no-cache 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==2.10.0
  • แก้ไขไฟล์ 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 = 'nuttachot@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)
  • สร้าง Bridge network โดยตั้งชื่อเป็น email_network
docker network create email_network
  • รัน Container ด้วย docker-compose และกลับไปที่ Terminal โดยใช้ parameter -d
docker-compose up -d
  • ดู Container ที่กำลังรันทั้งหมดที่ docker-compose.yml ดูแล
docker-compose ps
  • ดู Stacks (กลุ่มของ Container ที่ Config โดย Docker-compose) บน Docker ด้วย Portainer
  • ทดลองเรียก API ผ่าน Browser ตาม URL ดังนี้
http://labxx.cpsudevops.com:7001/docs
  • กด Try it out
  • กด Execute
  • ดู Record ที่บันทึกโดย Student Service จาก URL https://mydb.labxx.cpsudevops.com
  • ดู Record ที่บันทึกโดย Enroll Service จาก URL https://mydb2.labxx.cpsudevops.com
  • ดู email ใน Inbox
ขอขอบคุณ Nipa.Cloud ที่ให้การสนับสนุน Environment ในการเรียนการสอน
รายวิชา Dev-Ops and Cloud Engineering 101