Mini Project at Enterprise Architecture course, MUM.EDU.
Deployment Pre-request:
- Google Cloud Kubernetes (Standard trial version would be OK)
- Read the instructions (steps) carefully
- Ask if any trouble
(Automated deployment in the deployment folder!)
The deployment has several steps:
This CS 544 - Enterprise Architecture assignment #2 was crafted by Rustem Bayetov 610133 and Sodbileg Shirmen 610170.
We implemented web store where users can see catalog (products list), register, authenticate, place order and pay.
During this assignment we created 9 microservices, tested and deployed to Google Cloud Platform. The project is fully consistent with the assignment requirements.
In this project we used:
- Docker to build and containerize our services
- Kubernetes to run containerised images
- OpenFeign to discover services
- Kubernetes Configuration to manage secrets and configurations
- Spring security to authenticate users
- MySql as container
Extra:
- We configured pulling, compiling and deploying services from GitHub
- Have a good mood to score this work
- Our source code
- Good machine with minikube or Google cloud platform (or other Kubernetes engine powered platform)
Open Google Cloud shell and clone the source code from GitHub link: GitHub (ask us about accessing to git repository or use our provided source code)
Run following command:
$ cd ea2/deployment
Push all docker images for all microservices. It would compile source codes and push to Docker Hub, automatically.
$ make docker-push-all
Create static public IP address for gateway! If have problem you can use this IP address after deploying all services including ingress gateway.
$ make apply-static-IP
Deploy docker images to pods in Kubernetes cluster.
$ make kubectl-apply-all-services
It should be apply all pods and services with secrets which will be used to connect MySQL DB and running arguments for Spring Boot Rest Application (services).
Wait for mysql service runnig and then execute following command to create schema databases for running Spring Boot App.
$ make kubectl-create-init-db:
Apply for Ingress Proxy using following command.
$ make kubectl-apply-ingress
The ingress proxy should be delayd for creating related mappings for front end services. Check this status following command:
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
ea2-ingress * 34.102.228.59 80 161m
Now we can open any web browsers or postman to check functionality of our application.
- example:
-
http://34.102.228.59/products/list
--> can be point to our orders-service inside k8s
-
Result screenshot:
- apply
config/global-secrets.yaml
file to configure secrets - run mysql. apply
mysql/deployment.yaml
- run all services. Go to each service folder and apply
manifests/deployment.yaml
andmanifests/service.yaml
files.
To run project on kubernetes you need:
- go to each service and edit
application.properties
file. Uncomment kubernetes settings and comment out localhost settings. - in each service go to the
interfaces
package and check if there isFeignClient
annotation exists. Comment outurl
property. - rebuild all packages and push to hub.docker.com (use
make docker-push
command located each service folder). But you will need access to myrustembayetov
account repositories. If you wanna push to your account change it inmakefile
file. - after that follow
Run project on kubernetes from images
section (above)
To run project on localhost you need:
- go to each service and edit
application.properties
file. Uncomment localhost settings and comment out kubernetes settings. - run mysql database according on
application.poperties
file settings (user: sa, password: password). - in each service go to the
interfaces
package and check if there isFeignClient
annotation exists. Uncommenturl
property.
Use our Postman predefined requests collection https://www.getpostman.com/collections/35129f327bae6e52ad6b
-
See products catalog. Navigate to
http://localhost:8085/products/list
(if you run on localhost) orhttp://34.102.228.59/products/list
(if you run from kubernetes) -
Register new user. Navigate to
localhost:8081/auth/register
orhttp://34.102.228.59/auth/register
Payload:
{ "firstName": "Rustem", "lastName": "Bayetov", "email": "[email protected]", "password": "1234", "adrStreet": "1000 N 4th St", "adrCity": "Fairfield", "adrState": "IA", "adrZip": "52557", "paymentCreditCard": { "type": "cc", "cardNumber": "1234567812345678", "holderName": "rustem bayetov", "cvc": "123", "bankAccountNumber": "", "payPalUsername": "" }, "paymentBankAccount": { "type": "ba", "cardNumber": "", "holderName": "", "cvc": "", "bankAccountNumber": "12341234", "payPalUsername": "" }, "paymentPayPal": { "type": "pp", "cardNumber": "", "holderName": "", "cvc": "", "bankAccountNumber": "", "payPalUsername": "rustem.bayetov" }, "preferredPaymentMethod": "cc" }
-
Sign in. Navigate to
localhost:8081/auth/sign-in
orhttp://34.102.228.59/auth/sign-in
Payload:
{ "email": "[email protected]", "password": "1234" }
Grab given JWT token and use it for further calls. Use it as Bearer token in your requests.
-
Add products to catalog. Navigate to
http://localhost:8085/products/add
orhttp://34.102.228.59/products/products/add
Payload:
{ "name":"iPhone 11", "vendor": "Apple", "category":"Mobile phones", "availableCount":10, "price":800 }
-
Place an order. Navigate to
http://localhost:8086/orders/place-order
orhttp://34.102.228.59/orders/place-order
Payload:{ "userId":1, "usePreferredAddress": true, "adrStreet":"", "adrCity":"", "adrState": "", "adrZip":"", "usePreferredPaymentMethod": true, "payType":"ba", "payCardNumber":"", "payHolderName":"", "payCvc":"", "payPayPalUsername":"", "items": [ { "productId": 1, "quantity": 2 } ] }
When you placing order:
orders-service
check that user has valid JWTorders-service
check passed data.orders-service
retrieve user data fromauth-service
auth-service
check service secretauth-service
get user data frommysql
databaseauth-service
return user data
orders-service
check retrieved user dataorders-service
retrieve all products data fromproducts-service
orders-service
check all products available countorders-service
asksproducts-service
to decrease products countorders-service
determining user payment info (get from request or from user predefined data)orders-service
askspays-service
to make an paymentpays-service
based on payment method (Credit cardcc
, Bank Accountba
, PayPalpp
) calls on of 3 services:pays-cc-service
to make payment using Credit cardpays-ba-service
to make payment using Bank Accountpays-pp-service
to make payment using PayPal
orders-service
saving order to databaseorders-service
notifyingship-service
about placed orderorders-service
return Saved successfully message
PS:
- every service-to-service communication requires to use ServiceSecret.
auth-service
has his own schemausers
in databaseproducts-service
has his own schemaproducts
in databaseorders-service
has his own schemaorders
in database
- We decided to split databases by services because it gives us opportunity to increase project horizontally
- We split payment payments methods to different services because evert of them have to deal with 3rd party services (eg. Banks, PayPal). Some 3rd party endpoint can have different security requirements. For example: keep our endpoint isolated in network and physically.
- We decided keep all user authorization, sign in functionality in single service because this functions are coupled by logic. Also there are no huge calculation so this projects can live long until redesign.
- We decided use meSQL database because for this assignment requirements it is easy, convenient and enough powerful choice.
- We tested Rest template and OpenFeign service discovery methods and selected second one because it easier to use and it comes from Netflix.
- Unfortunately, we had no much time to compare and understand other techniques.
- To get products list (catalog)
GET products-service/products/list
- To get product by id
GET products-service/products/get/{id}
- To add product
POST products-service/products/add
. Put data in body. - To delete product by id
DELETE products-service/products/delete/{id}
Service secret required
On kubernetes: pays-service/pays_pp/pay
On localhost: http://localhost:8090/pays/pay
Payload:
{
"type":"cc",
"cardNumber":"1234567890123456",
"holderName":"Rustem",
"cvc":"123",
"bankAccountNumber": "12345678",
"payPalUsername":"rustem.bayetov"
}
Service secret required
On kubernetes: pays-cc-service/pays_cc/pay
On localhost: http://localhost:8091/pays_cc/pay
Payload:
{
"type": "cc",
"cardNumber": "1234567890123456",
"holderName": "Buus",
"cvc": "123",
"bankAccountNumber": "",
"payPalUsername": ""
}
Service secret required
On kubernetes: pays-ba-service/pays_ba/pay
On localhost: http://localhost:8092/pays_ba/pay
Payload:
{
"type":"ba",
"cardNumber":"",
"holderName":"",
"cvc":"",
"bankAccountNumber": "12345678",
"payPalUsername":""
}
Service secret required
On kubernetes: pays-pp-service/pays_pp/pay
On localhost: http://localhost:8093/pays_pp/pay
Payload:
{
"type": "pp",
"cardNumber": "",
"holderName": "",
"cvc": "",
"bankAccountNumber": "",
"payPalUsername": "rustem.bayetov"
}