Developing CNA - HiroSung/Study GitHub Wiki

Intro

Day1

EventStorming

  • MSA School - ์ผ๋ฐ˜์ ์ธ CNA ๊ตฌํ˜„
  • bounded context ๋‹จ์œ„๋กœ ์„œ๋น„์Šค์˜ ๋‹จ์œ„๋ฅผ ๊ตฌ์„ฑํ•จ.
  • EventStorming์˜ ๋งˆ์ง€๋ง‰์€
    • Policy๋Š” ๋ˆ„๊ฐ€ ํ•  ๊ฒƒ์ธ์ง€ ์ž‘์—…์ด ํ•„์š”ํ•จ. ==> Context Mapping
    • Event์™€ Policy๊ฐ„์— ์—ฐ๊ฒฐ์„ ํ•˜๋ฉด Pub/Sub ์ด ์ƒ์„ฑ๋จ. ==> ๋น„๋™๊ธฐ. Wating ์—†์Œ. ํ˜ธ์ถœํ•˜๊ณ  ๋๋‚จ. Queue๋ชจ๋“ˆ์ด ํ•„์š”ํ•จ.
    • Event์™€ Command๊ฐ„์— ์—ฐ๊ฒฐํ•˜๋ฉด Req/Res ==> ๋™๊ธฐ. Wating.
    • ์กฐํšŒ๋Š” Event๊ฐ€ ์•„๋‹˜.
    • ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์„ค๋ช…๋˜์–ด์•ผ ํ•จ.
      • ๊ณ ๊ฐ์ด ์ฃผ๋ฌธ์„ ํ•˜๊ฒŒ ๋˜๋ฉด "์ฃผ๋ฌธ๋จ"์ด๋ผ๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒํ•˜๊ณ , Ship๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐฐ์†ก์—์„œ๋А Shipped ๋ผ๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒํ•จ.
      • Event๋Š” Aggregate์—์„œ ๋ฐœ์ƒ๋จ.
  • CNA๊ฐ€ ์ง€ํ–ฅํ•˜๋Š” EventStorming์ด Arch๋Š” Pub/Sub ์ด๋‹ค.
  • RESTAPI
    • "http REST_METHOD, url"
    • Method : POST(์ƒ์„ฑ), GET(์กฐํšŒ), PUT/PATCH(์ˆ˜์ •), DELTE(์‚ญ์ œ)
  • HTTPie (https://httpie.org/)

์ผ๋ฐ˜์ ์ธ CNA ๊ตฌํ˜„

  • HTTPie ์„ค์น˜
  • Spring Initializer ์—์„œ POM ์ƒ์„ฑ - H2, JPA, Rest Repository ์ถ”๊ฐ€
  • ์ฝ”๋”ฉ
    • Aggregate ์ƒ์„ฑ
    • Command ์ƒ์„ฑ
      • CF : netstat -ano | findstr "PID :808"
    • MVN > demo > Plugins > spring-boot > spring-boot:run ์‹คํ–‰ํ•˜์—ฌ tomcat ์ˆ˜ํ–‰ํ•จ.
    • console์— "started" ๋ฉ”์‹œ์ง€ ํ™•์ธ
C:\CNA\demo>http http://localhost:8080
HTTP/1.1 200
Connection: keep-alive
Content-Type: application/hal+json
Date: Mon, 10 Aug 2020 05:46:54 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers

{
    "_links": {
        "products": {
            "href": "http://localhost:8080/products"
        },
        "profile": {
            "href": "http://localhost:8080/profile"
        }
    }
}
C:\CNA\demo>
  • Event ์ƒ์„ฑ
    • ์ด๋ฒคํŠธ์˜ ์ถœ์ฒ˜๋Š” Aggregate์ด๋ฉฐ, Aggregate ๊ฐ€์ง€๋Š” ์†์„ฑ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ.
    • ProductChanged
    • ์šฐ๋ฆฌํŒ€์˜ ์ด๋ฒคํŠธ๋Š” ํƒ€ํŒ€์˜ ์š”๊ตฌ์— ์˜ํ•ด์„œ ์˜คํ”ˆ(์ƒ์„ฑ)ํ•˜๊ฒŒ ๋จ.
  • Event ๋ฐœํ–‰ (Event Raise)
    • ๋‚˜ ๋‹ค์Œ์— ์ˆ˜ํ–‰ํ•  ๊ฒƒ์„ ์ง€์ •ํ•˜๋Š”๊ฒƒ Trigger ํ•˜๊ฒŒ๋จ.
    • POST / PATCH / DELETE
  • ์นดํ”„์นด ์„ค์น˜ / ์ฃผํ‚คํผ ์„ค์น˜ . ์‹คํ–‰
    • Middleware - Kafka
    • ์นดํ”„์นด๋Š” ๋ถ„์‚ฐ ํ™˜๊ฒฝ ์ฝ”๋””๋„ค์ดํ„ฐ(Coordinator)์ธ ์ฃผํ‚คํผ ์œ„์—์„œ ์ž‘๋™์„ ํ•ฉ๋‹ˆ๋‹ค. ์ฃผํ‚คํผ๋ฅผ ๋จผ์ € ์‹คํ–‰ํ•˜๊ณ , ์นดํ”„์นด๋ฅผ ์ด์–ด์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • POM.xml ์ˆ˜์ • ํ›„, Reimport ํ•ด์•ผํ•จ.
    • ์นดํ”„์นด ์„ค์น˜์‹œ ๊ธฐ๋ณธ ํฌํŠธ๊ฐ€ 9092

    #kafka-console-consumer.bat --bootstrap-server http://localhost:9092 --topic shop --from-beginning

    • http POST localhost:8080/products name=โ€œTVโ€ stock=10 ์ˆ˜ํ–‰ํ•˜์—ฌ ์ฝ˜์†”์— ์ฐํžˆ๋Š” ๋‚ด์šฉ ํ™•์ธ.
  • ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” Policy ๋ฅผ ์ƒ์„ฑ
    • Event์— ๋Œ€์‘๋˜๋Š” Policy(ํด๋ฆฌ์‹œ)๋Š” ๋‹ค๋ฅธ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค(ํŒ€)์—์„œ ์ˆ˜์‹  ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์ƒํ’ˆ ์„œ๋น„์Šค์—์„œ ProductChanged ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฃผ๋ฌธ์ด๋‚˜ ๋ฐฐ์†ก ์„œ๋น„์Šค์—์„œ ์ด๋ฅผ ์ˆ˜์‹  ํ›„ ๊ฐ ์„œ๋น„์Šค์— ๋งž๋Š” Biz-Logic์„ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ, ํŽธ์˜์ƒ Kafka๋กœ๋ถ€ํ„ฐ ๋ฉ”์„ธ์ง€ ์ˆ˜์‹ ๋งŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

Day2 - ๋„๊ตฌ๊ธฐ๋ฐ˜(MSAEz) CNA๊ตฌํ˜„

  • User Stories
  > ๊ณ ๊ฐ์ด ์ฃผ๋ฌธ์„ ํ•˜๋ฉด, ์ฃผ๋ฌธ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋ฐฐ์†ก์ด ์‹œ์ž‘๋œ๋‹ค.
  > ๊ณ ๊ฐ์ด ์ฃผ๋ฌธ์„ ์ทจ์†Œํ•˜๊ฒŒ๋˜๋ฉด, ์ฃผ๋ฌธ์ •๋ณด๋Š” ์‚ญ์ œ๋˜๋‚˜, ๋ฐฐ์†ก์—์„œ๋Š” (์‚ฌํ›„, ํ™œ์šฉ ์œ„ํ•ด) ์ทจ์†Œ์žฅ๋ถ€๋ฅผ ๋ณ„๋„ ์ €์žฅํ•œ๋‹ค.
  > ์ฃผ๋ฌธ์ทจ์†Œ๋Š” ๋ฐ˜๋“œ์‹œ ๋ฐฐ์†ก์ทจ์†Œ๊ฐ€ ์ „์ œ๋˜์–ด์•ผ ํ•œ๋‹ค. ==> feignclient ์‚ฌ์šฉ
  > ์ฃผ๋ฌธ๊ณผ ๋ฐฐ์†ก MSA๋Š” ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ํ†ตํ•ด ๊ณ ๊ฐ๊ณผ ํ†ต์‹ ํ•œ๋‹ค.

์ฃผ๋ฌธ/๋ฐฐ์†ก์„ ์œ„ํ•œ EventStorming ๋ชจ๋ธ๋ง

  • MSAEZ ํˆด์„ ํ™œ์šฉ (http://msaez.io/)
  • ์ด๋ฒคํŠธ Trigger ์ƒ์„ฑ์‹œ
    • Post Persist๋กœ ํ•ด์•ผ ์ƒ์„ฑ ํ›„, Key๊ฐ’์— ํ•ด๋‹นํ•˜๋Š” ์ฃผ๋ฌธ๋ฒˆํ˜ธ๋ฅผ Kafka๋กœ ์ „๋‹ฌํ•จ.
    • OrderCenceled์˜ Cancel command ๋Š” POST ๋กœ ์„ค์ •
  • PolicyHandler.java
    • policy๋Š” ์ด๋ฒคํŠธ์— ๊ท€๋ฅผ ๊ธฐ์šธ์ด๋Š” ์—ญํ• .
    • ์ฃผ๋ฌธ์˜ ๊ธฐ๋Šฅ์„ ๊ฐ€์ ธ์™€์„œ Delivery ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜๋Š”๊ฒƒ
    • ์ฃผ๋ฌธ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ๋กœ์ง์„ ๊ตฌํ˜„ ํ•˜๋Š” ๊ฒƒ.
    @StreamListener(KafkaProcessor.INPUT)   // kafka๋ฅผ ํ•ด๋ฐ”๋ผ๊ธฐ ํ•˜๊ณ  ์žˆ๋Š” ์ฝ”๋“œ
    public void wheneverOrdered_Ship(@Payload Ordered ordered){

        if(ordered.isMe()){
            System.out.println("##### listener Ship : " + ordered.toJson());
            // TODO 
        }
    }
  • context mapping ์ข…๋ฅ˜
    1. ordered ๊ฐ™์€ ๊ฒƒ์€ ์ฝ”๋ ˆ์˜ค๊ทธ๋ž˜ํ”ผ, choreography, : ์ค‘๊ฐ„์— ์—ฐ๊ฒฐํ•˜๊ณ  ์•Œ์•„์„œ ์ฒ˜๋ฆฌ
    2. ordercanceled ๊ฐ™์€๊ฒƒ์€ orchestration type : ๋ชจ๋“ ๊ฒƒ์„ ์ค‘๊ฐ„ ๊ด€๋ฆฌ
  • ํˆด : ํ”ผ๋ณดํƒˆ์˜ ๋ณด๋ฆฌ์Šค
  • ๋น„๊ธฐ๋Šฅ - ๊ฒŒ์ดํŠธ์›จ์ด
    • DMZ ๋ฐ–์— ๋…ธ์ถœ๋˜๋Š” IP์—์„œ ์„œ๋น„์Šค๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ์—ญํ• 
    • ์ฝฉ, IBM, Istio ingress , Nginx ...
  • Project > ์ฝ”๋“œ ๋ฅผ ์„ ํƒํ•˜๋ฉด , order, delivery ๋ฅผ ์ฝ”๋“œ๋กœ ์ž๋™ ์ƒ์„ฑํ•ด์คŒ.
    • order
    • delivery
    • gateway
    • application.yml ๋‚ด์šฉ ํ™•์ธ
    • docker ํŒŒ์ผ ๋‚ด์šฉ ํ™•์ธ

Intelij ์ฝ”๋”ฉ

  • ์†Œ์Šค ๋‹ค์šด๋กœ๋”ฉ
  • InteliJ ํด๋” ์„ ํƒ
  • ์Šคํƒ€ํŠธ
    • ์ฃผํ‚คํผ ์Šคํƒ€ํŠธ : zookeeper-server-start.bat ../../config/zookeeper.properties
    • ์นดํ”„์นด ์Šคํƒ€ํŠธ : kafka-server-start.bat ../../config/server.properties
    • ์ด๋ฒคํŠธ์ˆ˜์‹  ๋ชจ๋‹ˆํ„ฐ : kafka-console-consumer.bat --bootstrap-server http://localhost:9092 --topic local --from-beginning
    • pom.xml > plugin - spring-boot:run ์‹คํ–‰ (order, delivery, gateway)
      • Kafka ; ํ† ํ”ฝ : ํŠน์ • ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ. ๋ฉ”์‹œ์ง€๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ํ†ต๋กœ
# netstat -ano | findstr PID "808"
# http GET http://localhost:8081
# http POST http://localhost:8081/orders productId=1001 qty=10
  • order์— ์žˆ๋Š” Ordered๊ฐ€ delivery์— Ordered๋กœ ๋˜‘๊ฐ™์ด ์กด์žฌํ•จ. why?
    • Contract test
    • Event & Policy ๊ทœ์•ฝ์ž„
  • ๋™๊ธฐํ˜ธ์ถœ
    • spring ์—์„œ RestTemplate
    • cloud๋Š” FeignClient ์‚ฌ์šฉํ•จ.
    • ์„œ๋ฒ„ to ์„œ๋ฒ„
    • interface, FeignClient ์„ค์ •ํ•˜๋ฉด ๋จ.
@FeignClient(name="delivery", url="http://localhost:8082")  // cloud ์•ˆ์—์„œ ํ˜ธ์ถœํ•˜๋Š” ์ฃผ์†Œ.  delivery
public interface CancellationService {

    @RequestMapping(method= RequestMethod.POST, path="/cancellations")
    public void cancel(@RequestBody Cancellation cancellation);

}
  • Order ํ•˜๋Š” ๋ถ€๋ถ„
    @PreRemove
    public void onPreRemove(){
        OrderCanceled orderCanceled = new OrderCanceled();
        BeanUtils.copyProperties(this, orderCanceled);
        orderCanceled.publishAfterCommit();

        //Following code causes dependency to external APIs
        // it is NOT A GOOD PRACTICE. instead, Event-Policy mapping is recommended.

        local.external.Cancellation cancellation = new local.external.Cancellation();
        // mappings goes here. ์•„๋ž˜๋‚ด์šฉ ์ถ”๊ฐ€
        cancellation.setOrderId(this.getId());
        cancellation.setStatus("DELIVERY CANCELLED");

        OrderApplication.applicationContext.getBean(local.external.CancellationService.class)
            .cancel(cancellation);


    }

  • order ์žฌ์‹œ์ž‘
# http POST http://localhost:8081/orders productId=1 qty=10
# http POST http://localhost:8081/orders productId=2 qty=10
# http DELETE http://localhost:8082/cancellations/1
  • CancelationService url์„ yml ์— ์ฐธ์กฐํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •

Day3

  • CQRS, MView

  • User Stories

  > ๊ณ ๊ฐ์ด ์ฃผ๋ฌธ์„ ํ•˜๋ฉด, ์ฃผ๋ฌธ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋ฐฐ์†ก์ด ์‹œ์ž‘๋œ๋‹ค.
  > ๊ณ ๊ฐ์ด ์ฃผ๋ฌธ์„ ์ทจ์†Œํ•˜๊ฒŒ๋˜๋ฉด, ์ฃผ๋ฌธ์ •๋ณด๋Š” ์‚ญ์ œ๋˜๋‚˜, ๋ฐฐ์†ก์—์„œ๋Š” (์‚ฌํ›„, ํ™œ์šฉ ์œ„ํ•ด) ์ทจ์†Œ์žฅ๋ถ€๋ฅผ ๋ณ„๋„ ์ €์žฅํ•œ๋‹ค.
  > ์ฃผ๋ฌธ์ทจ์†Œ๋Š” ๋ฐ˜๋“œ์‹œ ๋ฐฐ์†ก์ทจ์†Œ๊ฐ€ ์ „์ œ๋˜์–ด์•ผ ํ•œ๋‹ค. ==> feignclient ์‚ฌ์šฉ
  > ์ฃผ๋ฌธ๊ณผ ๋ฐฐ์†ก MSA๋Š” ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ํ†ตํ•ด ๊ณ ๊ฐ๊ณผ ํ†ต์‹ ํ•œ๋‹ค.

  ์ถ”๊ฐ€์š”์ฒญ์‚ฌํ•ญ
  > 

์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ๊ตฌํ˜„ ์ƒ˜ํ”Œ

View - Mypage

  • MSAEZ์—์„œ
    • View ์ถ”๊ฐ€ํ•˜์—ฌ
    • CQRS ์„ ํƒ
    • ์†์„ฑ orderId, qty, deliveryId, status, productId
    • create when
      • ์ฃผ๋ฌธ์ด ๋ฐœ์ƒํ–ˆ์„๋•Œ View Field์—์„œ EvnetField์— ๋งคํ•‘
      • Ordered - orderId = id, qty = qty
    • update when
      • Shipped - status = Sipped.status
      • DeliveryCanceled - orderId = orderId
    • boundedcontext ์ถ”๊ฐ€
      • customercenter
  • ์ฝ”๋“œ preview
    • customercenter๊ฐ€ ์ถ”๊ฐ€๋จ
    • ViewHandler ์ถ”๊ฐ€๋จ

์ฝ”๋”ฉ

  • mypage๋ฅผ InteliJ์— ์ถ”๊ฐ€ํ•˜๊ณ  gateway application.yml ์— ๋ผ์ดํŒ…์ •๋ณด ์ถ”๊ฐ€
---

spring:
  profiles: default
  cloud:
    gateway:
      routes:
        - id: order
          uri: http://localhost:8081
          predicates:
            - Path=/orders/** 
        - id: delivery
          uri: http://localhost:8082
          predicates:
            - Path=/deliveries/**,/cancellations/**
        - id: customercenter
          uri: http://localhost:8083
          predicates:
            - Path=/mypage/**
---

spring:
  profiles: docker
  cloud:
    gateway:
      routes:
        - id: order
          uri: http://order:8080
          predicates:
            - Path=/orders/** 
        - id: delivery
          uri: http://delivery:8080
          predicates:
            - Path=/deliveries/**,/cancellations/**
        - id: customercenter
          uri: http://customercenter:8080
          predicates:
            - Path=/mypage/**

git์— ์˜ฌ๋ฆฌ๊ธฐ

  • ubuntu์— mvn ์„ค์น˜
    • ์—†๋‹ค๋ฉด ์„ค์น˜ : sudo apt-get install maven
  • github์— repository ํ•˜๋‚˜ ์ƒ์„ฑ
    • local\order ํด๋”๋กœ ์ด๋™
# git init
# git status
# git add .
# git commit -m "message"
# git remote add origin https://github.com/Hirosung/cna-customercenter.git
# git push -u origin master

ubuntu์—์„œ dockerizing

  • git์— ์˜ฌ๋ฆฐ ์†Œ์Šค ๋‹ค์šด ๋ฐ›๊ธฐ

    git clone https://github.com/Hirosung/cna-order.git

  • cna-order ํด๋”๋กœ ์ด๋™
  • mvn์œผ๋กœ ์ปดํŒŒ์ผ ์•„์นด์ด๋น™

    mvn package

  • ๋„์ปค๋ผ์ด์ง• ํ•˜๊ธฐ
  • admin26.azurerc.io/cna-order:v1

docker build -t admin26.azurerc.io/cna-order:v1 .

  • ํ™•์ธ

docker images

  • push

docker push admin26.azurecr.io/cna-order:v1

  • ๊ธฐ์กด ์ด๋ฏธ์ง€ ์‚ญ์ œ

    docker images

Azure ๋กœ๊ทธ์ธ

  • ๋กœ๊ทธ์ธ
# az login -u [email protected] -p skccadmin1234!
# az aks get-credentials --resource-group admin26-rg --name admin26-aks
# kubectl config current-context
	> admin26-aks 
# kubectl get all
# kubectl get node
# kubectl create deploy my-nginx --image=nginx
# kubectl expose deploy my-nginx --port=8080
# kubectl create deploy my-nginx2 --image=nginx
# kubectl exec -it ... my-nginx2 -- /bin/bash
root@my-nginx2.. # curl http://my-nginx
root@my-nginx2.. # curl http://my-nginx.default.svc.cluster.local
                         ์„œ๋น„์Šค๋ช….๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ช…
# az acr login --name admin26

ubuntu์—์„œ kafka

  • ํ—ฌ๋ฆ„ ์„ค์ •
    • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์— ์นดํ”„์นด ์„ค์น˜ ๋ฐ ์‹คํ–‰
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash
kubectl --namespace kube-system create sa tiller      # helm ์˜ ์„ค์น˜๊ด€๋ฆฌ์ž๋ฅผ ์œ„ํ•œ ์‹œ์Šคํ…œ ์‚ฌ์šฉ์ž ์ƒ์„ฑ
kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
helm init --service-account tiller

kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
helm repo update
helm install --name my-kafka --namespace kafka incubator/kafka
  • ์ˆ˜ํ–‰๋œ kafka ํ™•์ธ

watch kubectl get all -n kafka

์—ฌ๊ธฐ์„œ ๋ถ€ํ„ฐ๋Š” ๋ชจ๋ฅด๊ฒ ๋‹ค...

Circuit Breaker

kiali

siege