cej55 / roaming

0 stars 0 forks source link

roaming

로밍

Table of contents

서비스 시나리오

기능적 요구사항

  1. 고객이 로밍대여일자/로밍국가/단말기를 선택하여 예약을 한다.
  2. 고객이 결제한다.
  3. 예약이 되면 예약 내역이 공항 부스에 전달된다.
  4. 공항 부스에선든 예약 정보를 바탕으로 고객에게 단말기를 대여한다.
  5. 고객이 예약을 취소할 수 있다
  6. 예약이 취소되면 로밍 예약정보가 취소된다.
  7. 고객이 단말기를 반납지에 반납한다.
  8. 공항 부스는 반납된 단말기를 회수한다.
  9. 고객은 로밍 서비스를 통해 상태를 중간중간 열람할 수 있어야 한다.

비기능적 요구사항

1.트랜잭션 결제가 되지 않은 예약건은 아예 거래가 성립되지 않아야 한다 Sync 호출

  1. 장애격리 로밍관리 기능이 수행되지 않더라도 로밍 예약은 365일 24시간 받을 수 있어야 한다 Async (event-driven), Eventual Consistency 결제 시스템이 과중되면 예약을 잠시동안 받지 않고 잠시 후 에 하도록 유도한다 Circuit breaker, fallback

  2. 성능 고객이 예약상태를 예약시스템(프론트엔드)에서 확인할 수 있어야 한다 CQRS

체크포인트

분석/설계

AS-IS 조직 (Horizontally-Aligned)

image

TO-BE 조직 (Vertically-Aligned)

image

Event Storming 결과

완성된 모형

image

완성본에 대한 기능적/비기능적 요구사항을 커버하는지 검증

기능적

고객이 로밍대여일자/로밍국가/단말기를 선택하여 예약을 한다.(ok)
고객이 결제한다.(ok)
예약이 되면 예약 내역이 공항 부스에 전달된다.(ok)
공항 부스에선든 예약 정보를 바탕으로 고객에게 단말기를 대여한다.(ok)

고객이 예약을 취소할 수 있다(ok)
예약이 취소되면 로밍 예약정보가 취소된다.(ok)
고객은 로밍 서비스를 통해 상태를 중간중간 열람할 수 있어야 한다.(ok)(view)

고객이 단말기를 공항부스에 반납한다.(ok)
공항 부스는 반납된 단말기를 회수한다.(ok)

비기능 요구사항에 대한 검증

비기능적 요구사항 검증

- 마이크로 서비스를 넘나드는 시나리오에 대한 트랜잭션 처리
    1. 고객 예약시 결제처리:  결제가 완료되지 않은 예약은 절대 받지 않는다는 경영자의 오랜 신념에 따라, ACID 트랜잭션 적용. 예약완료시 결제처리에 대해서는 Request-Response 방식 처리
    2. 결제 완료시 대여점 연결:  Reservation(front) 에서 Rental 마이크로서비스로 대여요청이 전달되는 과정에 있어서 Rental 마이크로 서비스가 별도의 배포주기를 가지기 때문에 Eventual Consistency 방식으로 트랜잭션 처리함.(Pub/Sub 방식)
    3. 나머지 모든 inter-microservice 트랜잭션: 예약상태, 렌탈상태 등 모든 이벤트에 대해 Customer 마이크로 서비스 에서 확인될수 있도록 , 데이터 일관성의 시점이 크리티컬하지 않은 모든 경우가 대부분이라 판단, Eventual Consistency 를 기본으로 채택함.

헥사고날 아키텍처 다이어그램 도출

캡쳐

- Chris Richardson, MSA Patterns 참고하여 Inbound adaptor와 Outbound adaptor를 구분함
- 호출관계에서 PubSub 과 Req/Resp 를 구분함
- 서브 도메인과 바운디드 컨텍스트의 분리:  각 팀의 KPI 별로 아래와 같이 관심 구현 스토리를 나눠가짐

구현:

분석/설계 단계에서 도출된 헥사고날 아키텍처에 따라, 각 BC별로 대변되는 마이크로 서비스들을 스프링부트으로 구현하였다. 구현한 각 서비스를 로컬에서 실행하는 방법은 아래와 같다 (각자의 포트넘버는 local port : 8081 ~ 8084 이다)

cd customer
mvn spring-boot:run

cd payment
mvn spring-boot:run 

cd romrental
mvn spring-boot:run  

cd reservation
mvn spring-boot:run 

DDD 의 적용

package roaming;

import javax.persistence.*;
import org.springframework.beans.BeanUtils;
import java.util.List;
import java.util.Date;

@Entity
@Table(name="Payment_table")
public class Payment {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String reserveId;
    private String phoneId;
    private String amount;
    private String userPhone;
    private String payType;
    private String payNumber;
    private String payCompany;
    private String payStatus;
    private String payDate;
    private String payCancelDate;

    @PostUpdate
    public void onPostUpdate(){
        if ("PayCanled".equals(this.getpayStatus()))
        {
            Paycanceled Paycanceled = new Paycanceled();
            BeanUtils.copyProperties(this, Paycanceled);
            Paycanceled.publishAfterCommit();
        }
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    public String getreserveId() {
        return reserveId;
    }

    public void setreserveId(String reserveId) {
        this.reserveId = reserveId;
    }
    public String getphoneId() {
        return phoneId;
    }

    public void setphoneId(String phoneId) {
        this.phoneId = phoneId;
    }
    public String getamount() {
        return amount;
    }

    public void setamount(String amount) {
        this.amount = amount;
    }
    public String getuserPhone() {
        return userPhone;
    }

    public void setuserPhone(String userPhone) {
        this.userPhone = userPhone;
    }
    public String getpayType() {
        return payType;
    }

    public void setpayType(String payType) {
        this.payType = payType;
    }
    public String getpayNumber() {
        return payNumber;
    }

    public void setpayNumber(String payNumber) {
        this.payNumber = payNumber;
    }
    public String getpayCompany() {
        return payCompany;
    }                                                                                                                                                            1,1           
    public void setpayCompany(String payCompany) {
        this.payCompany = payCompany;
    }
    public String getpayStatus() {
        return payStatus;
    }

    public void setpayStatus(String payStatus) {
        this.payStatus = payStatus;
    }
    public String getpayDate() {
        return payDate;
    }

    public void setpayDate(String payDate) {
        this.payDate = payDate;
    }
    public String getpayCancelDate() {
        return payCancelDate;
    }

    public void setpayCancelDate(String payCancelDate) {
        this.payCancelDate = payCancelDate;
    }

import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel="payments", path="payments") public interface PaymentRepository extends PagingAndSortingRepository<Payment, Long>{

Payment findByreserveId(String reserveId);

}

- 적용 후 REST API 의 테스트 : (참고-중간에 서비스재기동하여 IP변경됨 20.194.1.155->20.194.111.173)

  - reservation 서비스의 예약처리
   http://20.194.111.173:8080/reserve_action.html

   ![image](https://user-images.githubusercontent.com/84000910/124544832-dcf2e800-de62-11eb-8093-33326629f45d.png)

   ![image](https://user-images.githubusercontent.com/84000910/124544794-c77dbe00-de62-11eb-99cf-44b617415f39.png)

  - rental 서비스의 로밍대여처리
    http://20.194.111.173:8080/romrentals

    ![image](https://user-images.githubusercontent.com/84000910/124544909-0875d280-de63-11eb-8f2f-7a7fd1b8b869.png)

  - reservation 서비스의 반납처리
    http://20.194.111.173:8080/return_action.html

    ![image](https://user-images.githubusercontent.com/84000910/124545493-17a95000-de64-11eb-9849-51079185f8dc.png)

    ![image](https://user-images.githubusercontent.com/84000910/124545536-314a9780-de64-11eb-9699-618410e43fc2.png)

## CQRS

  - 예약/대여 상태 확인을 위한 CQRS구현(myPages)
    http://20.194.111.173:8080/mypages

    ![image](https://user-images.githubusercontent.com/84000910/124545089-58ed3000-de63-11eb-8b73-eccd88f55da7.png)

## Gateway

gateway application.yml 각 서비스별  rest api url 경로 추가
![image](https://user-images.githubusercontent.com/84000910/124412003-8c0ec100-dd88-11eb-9777-0eae4b3efc50.png)

- 빌드
mvn package -Dmaven.test.skip=true

- Docker 이미지 생성 및 ACR 등록
az acr build --registry user1414acr --image user1414acr.azurecr.io/roaming-gateway:latest --file Dockerfile .

- Deployment 배포
kubectl create deploy gateway --image=user1414acr.azurecr.io/roaming-gateway:latest -n ns-roaming

- Service 배포
kubectl expose deploy gateway --type="LoadBalancer" --port=8080 -n ns-roaming

- Service External IP 확인
kubectl get svc -n ns-roaming

![image](https://user-images.githubusercontent.com/84000910/124413144-080a0880-dd8b-11eb-8d55-fdc59344a3e1.png)

결과 확인

![image](https://user-images.githubusercontent.com/84000910/124420202-10b60b00-dd9a-11eb-9722-5f00e44b2aad.png)
![image](https://user-images.githubusercontent.com/84000910/124420235-24fa0800-dd9a-11eb-9ec8-e10066d34d62.png)
![image](https://user-images.githubusercontent.com/84000910/124420304-3c38f580-dd9a-11eb-80a8-ad99d7d0f83c.png)
![image](https://user-images.githubusercontent.com/84000910/124420694-e87adc00-dd9a-11eb-9336-64da0fe3ae63.png)

## 폴리글랏 퍼시스턴스

앱프런트 (app) 는 서비스 특성상 많은 사용자의 유입과 상품 정보의 다양한 콘텐츠를 저장해야 하는 특징으로 인해 H2 DB와 HSQL DB에 부착시켰다.
reservation : HSQL DB
나머지 payment/romrental/customer : H2 DB

## reservation pom.xml

![폴리그랏 캡쳐 화면](https://user-images.githubusercontent.com/84000910/124422791-de5adc80-dd9e-11eb-9685-02d0dda677f9.PNG)

## 동기식 호출 과 Fallback 처리

분석단계에서의 조건 중 하나로 예약(Reservation)->결제(Payment) 간의 호출은 동기식 일관성을 유지하는 트랜잭션으로 처리하기로 하였다. 호출 프로토콜은 이미 앞서 Rest Repository 에 의해 노출되어있는 REST 서비스를 FeignClient 를 이용하여 호출하도록 한다. 

- 결제서비스를 호출하기 위하여 Stub과 (FeignClient) 를 이용하여 Service 대행 인터페이스 (Proxy) 를 구현 

(Reservation) PaymentService.java

package roaming.external;

import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestBody; //import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam;

import java.util.Date;

//@FeignClient(name="payment", url="http://localhost:8083") // payment url => http://localhost:8083 @FeignClient(name="payment", url="${api.payment.url}") // payment url => http://localhost:8083 public interface PaymentService {

@RequestMapping(method= RequestMethod.POST, path="/pay")
public boolean pay(@RequestParam("reserveId") String reserveId,
            @RequestParam("phoneId") String phoneId,
            @RequestParam("userPhone") String userPhone,
            @RequestParam("amount") String amount,
            @RequestParam("payType") String userpayType,
            @RequestParam("payNumber") String payNumber,
            @RequestParam("payCompany") String payCompany);

}


- 예약을 받은 직후 1차로 예약 정보를 저장하고, 결제서비스에 결재 요청을 하여 결재 정상이면 예약 상태를 Reserved 로, 실패하면 ReserveFailed 로 상태로 갱신한다.
  ReservationController에서 구현함
  예약 상태가 Reserved 일때만 이벤트가 송신되며, ReserveFailed 일때는 이벤트 송신을 하지 않는다.

ReservationController.java (Controller)

@RequestMapping(value = "/reserve",
method = RequestMethod.POST,
produces = "application/json;charset=UTF-8")

public String reserve(HttpServletRequest request, HttpServletResponse response)
    throws Exception {
    System.out.println("##### /reserve  called #####");

    String phoneId = request.getParameter("phoneId");
    String rentalAddr = request.getParameter("rentalAddr");
    String retrieveAddr = request.getParameter("retriveAddr");
    String userPhone = request.getParameter("userPhone");
    String amount = request.getParameter("amount");
    String reserveDate = request.getParameter("reserveDate");
    String payType = request.getParameter("payType");
    String payNumber = request.getParameter("payNumber");
    String payCompany = request.getParameter("payCompany");

    System.out.println("##### phoneId : " + phoneId);
    System.out.println("##### amount : " + amount);
    System.out.println("##### userPhone : " + userPhone);
    System.out.println("##### payType : " + payType);
    System.out.println("##### payNumber : " + payNumber);
    System.out.println("##### payCompany : " + payCompany);

    Reservation Reservation = new Reservation();
    Reservation.setphoneId(phoneId);
    Reservation.setuserPhone(userPhone);

    Reservation.setamount(amount);

    Reservation.setreserveDate(reserveDate);
    Reservation.setpayType(payType);
    Reservation.setpayNumber(payNumber);
    Reservation.setpayCompany(payCompany);

    Reservation  = ReservationRepository.save(Reservation);

    String reserveId = Long.toString(Reservation.getId());
    System.out.println("##### reserveId : " + reserveId);

    boolean ret = false;
    try {
        ret = ReservationApplication.applicationContext.getBean(roaming.external.PaymentService.class)
            .pay(reserveId, phoneId, amount, userPhone, payType, payNumber, payCompany);

        System.out.println("##### /payment/pay  called result : " + ret);
    } catch (Exception e) {
        System.out.println("##### /payment/pay  called exception : " + e);
    }

    String status = "";
    if (ret) {
        status = "Reserved";
    } else {
        status = "ReserveFailed";
    }

    Reservation.setreserveStatus(status);
    Reservation  = ReservationRepository.save(Reservation);

    return status + " ReserveNumber : " + reserveId;
}

-- Reservation.java에서 이벤트 송시

@PostUpdate
public void onPostUpdate(){
    if ("Reserved".equals(this.getReserveStatus()))
    {
        Reserved reserved = new Reserved();
        BeanUtils.copyProperties(this, reserved);
        reserved.publishAfterCommit();
        System.out.println("##### send event : Reserved  #####");   
    } 
    else if ("ReserveCanceled".equals(this.getReserveStatus()))
    {
        ReserveCanceled reserveCanceled = new ReserveCanceled();
        BeanUtils.copyProperties(this, reserveCanceled);
        reserveCanceled.publishAfterCommit();
    }               
    else if ("ReserveReturned".equals(this.getReserveStatus()) )
    {
        ReserveReturned reserveReturned = new ReserveReturned();
        BeanUtils.copyProperties(this, reserveReturned);
        reserveReturned.publishAfterCommit();
        System.out.println("##### send event : ReserveReturned  #####");  
    }             
}    

- 동기식 호출에서는 호출 시간에 따른 타임 커플링이 발생하며, 결제 시스템이 장애가 나면 예약이 안되는 것을 확인:

결제 (Payment) 서비스 중지

![image](https://user-images.githubusercontent.com/84000909/122337244-06fa6e00-cf79-11eb-984b-a699e14fabbe.png)

예약처리

#결제서비스 

image


#예약처리
- 요청

image

- 결과

image

비동기식 호출 테스트

예약이 이루어진 후에 대여점으로 이를 알려주는 행위는 동기식이 아니라 비 동기식으로 처리하여 Rental 서비스가 장애시에도 예약/결제가 블로킹 되지 않아도록 처리한다.

import javax.persistence.*; import org.springframework.beans.BeanUtils; import java.util.List; import java.util.Date;

@Entity @Table(name="Reservation_table") public class Reservation {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String carId;
private String rentalAddr;
private String retrieveAddr;
private String userPhone;
private Long amount;
private String payType;
private String payNumber;
private String payCompany;
private String reserveDate;
private String cancelDate;
private String returnDate;
private String reserveStatus;

@PostUpdate
public void onPostUpdate(){
    if ("Reserved".equals(this.getReserveStatus()))
    {
        Reserved reserved = new Reserved();
        BeanUtils.copyProperties(this, reserved);
        reserved.publishAfterCommit();
        System.out.println("##### send event : Reserved  #####");   
    } 
    else if ("ReserveCanceled".equals(this.getReserveStatus()))
    {
        ReserveCanceled reserveCanceled = new ReserveCanceled();
        BeanUtils.copyProperties(this, reserveCanceled);
        reserveCanceled.publishAfterCommit();
    }               
    else if ("ReserveReturned".equals(this.getReserveStatus()) )
    {
        ReserveReturned reserveReturned = new ReserveReturned();
        BeanUtils.copyProperties(this, reserveReturned);
        reserveReturned.publishAfterCommit();
        System.out.println("##### send event : ReserveReturned  #####");  
    }             
}
- Rental 서비스에서는 예약됨(Reserved) 이벤트를 수신하여 자신의 정책을 처리하도록(RentalAccepted) PoliyHandler 를 구현한다.
 예약정보를 DB에 RentalAccepted 상태로 저장한 후, 이후 처리는 해당 Aggregate 내에서 한다(이벤트 송출).

< Rental PolicyHanlder.java> package carsharing;

import carsharing.config.kafka.KafkaProcessor;

import java.time.LocalDate;

import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Service;

@Service public class PolicyHandler{ @Autowired RentalRepository rentalRepository;

@StreamListener(KafkaProcessor.INPUT)
public void wheneverReserved_AcceptRental(@Payload Reserved reserved){

    if(!reserved.validate()) return;

    System.out.println("\n\n##### listener AcceptRental : " + reserved.toJson() + "\n\n");

    String reserveId = Long.toString(reserved.getId());
    String carId = reserved.getCarId();
    String rentalAddr = reserved.getRentalAddr();
    String retrieveAddr = reserved.getRetrieveAddr();
    String userPhone = reserved.getUserPhone();
    Long amount = reserved.getAmount();
    String payType = reserved.getPayType();
    String payNumber = reserved.getPayNumber();
    String payCompany = reserved.getPayCompany();
    String reserveDate = reserved.getReserveDate();

    Rental rental = new Rental();
    rental.setReserveId(reserveId);
    rental.setCarId(carId);
    rental.setRentalAddr(rentalAddr);
    rental.setRetrieveAddr(retrieveAddr);
    rental.setUserPhone(userPhone);
    rental.setAmount(amount);
    rental.setPayType(payType);
    rental.setPayNumber(payNumber);
    rental.setPayCompany(payCompany);
    rental.setReserveDate(reserveDate);
    LocalDate localDate = LocalDate.now();                
    rental.setRentAcceptDate(localDate.toString());
    rental.setRentalStatus("RentalAccepted");
    rentalRepository.save(rental);           

    System.out.println("##### rental accepted by reservation reserve #####");
    System.out.println("reserveId : " + reserveId);             
}

}


렌탈 서비스는 예약/결제와 완전히 분리되어 있으며, 이벤트 수신에 따라 처리되기 때문에, Rental서비스가 유지보수로 인해 잠시 내려간 상태라도 예약을 받는데 문제가 없다:

#Rental 서비스 (rental)의 중지
![image](https://user-images.githubusercontent.com/84000909/122337843-d7983100-cf79-11eb-8bac-95d62352d286.png)

#예약서비스에서 예약 처리

![image](https://user-images.githubusercontent.com/84000909/122337932-f1d20f00-cf79-11eb-9c77-acdcf45f064b.png)

예약처리 결과

ㅡ고객서비스에서 예약됨(Reserved) 확인됨

![image](https://user-images.githubusercontent.com/84000909/122337961-fb5b7700-cf79-11eb-9632-221112b2ff26.png)

#예약상태 확인 : status = Reserved 이다
![image](https://user-images.githubusercontent.com/84000909/122338051-1c23cc80-cf7a-11eb-8489-27e331dcf01f.png)

## Rental 재기동
status = RentalAccepted 로 변경됨
![image](https://user-images.githubusercontent.com/84000909/122338141-3fe71280-cf7a-11eb-9c60-dd5d55844779.png)

재기동 후 고객(Customer)서비스에서 결과 RentalAccepted 확인됨

![image](https://user-images.githubusercontent.com/84000909/122338179-4f665b80-cf7a-11eb-8d9d-3344b24e3b59.png)

# 운영

## Deploy : Gateway는 모든 서비스 배포 후에 빌드후 배포해야 함

- azure login 후 azure 클러스터/컨테이너 레지스트리 설정작업 진행
![image](https://user-images.githubusercontent.com/84000909/122336003-1f698900-cf77-11eb-842d-3db2758282ad.png)
kubelctl get all -n ns-carsharing  결과
![image](https://user-images.githubusercontent.com/84000909/122359556-9d865980-cf90-11eb-9b56-be9227efd0c7.png)

## Config Map (Reservation 배포전에 선배포)

* Config Map을 환경변수 등록함
kubectl create configmap cm-roaming --namespace="ns-roaming" --from-literal=DB_IP=10.20.30.1 --from-literal=DB_SERVICE_NAME=ROAMING

kubectl get cm -n ns-roaming

![image](https://user-images.githubusercontent.com/84000910/124408540-67631b00-dd81-11eb-8b51-ea77c90b5630.png)

* 등록된 Config Map을 조회함

![image](https://user-images.githubusercontent.com/84000910/124420981-85d61000-dd9b-11eb-92e3-84d25fae1d1f.png)

## 서킷 브레이커 : 장애격리(예약 시스템 장애 발생시)

1. Istio 설치

$ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.7.1 TARGET_ARCH=x86_64 sh - $ cd istio-1.7.1 $ export PATH=$PWD/bin:$PATH $ istioctl install --set profile=demo 1-1. 설치확인 $ kubectl get pod -n istio-system

2. Istio 모니터링 툴 설치

vi samples/addons/kiali.yaml

4라인의 apiVersion: apiextensions.k8s.io/v1beta1 을 apiVersion: apiextensions.k8s.io/v1으로 수정 kubectl apply -f samples/addons :오류가 날시는 아래의 명령어로 사용함 kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/kiali.yaml


2-2. 모니터링 툴 설정

kubectl edit svc kiali -n istio-system :%s/ClusterIP/LoadBalancer/g :wq!

2-3. 모니터링 시스템 접속
![image](https://user-images.githubusercontent.com/84000910/124495124-abdacf00-ddf2-11eb-8a11-7fb190ff4518.png)
![image](https://user-images.githubusercontent.com/84000910/124495159-bac18180-ddf2-11eb-9062-d39a8dedc80d.png)

http://20.194.106.104:20001/kiali (admin/admin)

3. 네임스페이스 생성

kubectl create namespace istio-test-ns kubectl label namespace istio-test-ns istio-injection=enabled

label에 istio-injection enabled 확인 kubectl get namespace istio-test-ns -o yaml

![image](https://user-images.githubusercontent.com/84000910/124490560-34ef0780-dded-11eb-8427-c228e59be98a.png)

4 namespace로 서비스 재배포

kubectl create deploy gateway --image=user1414acr.azurecr.io/roaming-gateway:latest -n istio-test-ns kubectl expose deploy gateway --type="LoadBalancer" --port=8080 -n istio-test-ns kubectl create deploy siege --image=apexacme/siege-nginx -n istio-test-ns

생성된 Container 확인
![image](https://user-images.githubusercontent.com/84000910/124490938-a7f87e00-dded-11eb-8fc5-ea025d5fe4ab.png)
![image](https://user-images.githubusercontent.com/84000910/124491698-86e45d00-ddee-11eb-8443-ce35204df721.png)

5. Circuit Breaker Destination Rule 생성

kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: dr-httpbin namespace: istio-test-ns spec: host: gateway trafficPolicy: connectionPool: http: http1MaxPendingRequests: 1 maxRequestsPerConnection: 1 EOF

5-1. Siege Client 접속

kubectl exec -it siege-88f7fdd8d-k45jb -n istio-test-ns -- /bin/bash


IP주소 확인
![image](https://user-images.githubusercontent.com/84000909/122350982-a8d58700-cf88-11eb-97d2-1847405c0153.png)

정상 동작일떄 확인
siege -c1 -t30S -v --content-type "application/json" 'http://52.141.59.66:8080/reservations POST {"resevedId": "010", "amonut": "1"}'

![image](https://user-images.githubusercontent.com/84000910/124493509-ac726600-ddf0-11eb-8ddc-96716ac36393.png)

Circuit Breaker 동작 확인
siege -c2 -t50S -v --content-type "application/json" 'http://52.141.59.66:8080/reservations POST {"resevedId": "010", "amonut": "1"}'

![image](https://user-images.githubusercontent.com/84000910/124493753-f4918880-ddf0-11eb-9ef5-296bce88bf9d.png)

6.모니터링 시스템 (kiali)에서 확인
![image](https://user-images.githubusercontent.com/84000910/124494970-77ffa980-ddf2-11eb-88b7-14b088703474.png)

운영시스템은 죽지 않고 지속적으로 CB 에 의하여 적절히 회로가 열림과 닫힘이 벌어지면서 자원을 보호하고 있음을 보여줌. 하지만, 62.45% 가 성공하였고, 37%가 실패했다는 것은 고객 사용성에 있어 좋지 않기 때문에 Retry 설정과 동적 Scale out (replica의 자동적 추가,HPA) 을 통하여 시스템을 확장 해주는 후속처리가 필요.

### 오토스케일
앞서 CB 는 시스템을 안정되게 운영할 수 있게 해줬지만 사용자의 요청을 100% 받아들여주지 못했기 때문에 이에 대한 보완책으로 자동화된 확장 기능을 적용하고자 한다. 

- Reservation Deloypment.yaml 의 오토스케일을 위한 pod 초기 cpu, max cpu 설정 적용
![image](https://user-images.githubusercontent.com/84000910/124495393-02480d80-ddf3-11eb-93e5-bb3c3f029bc6.png)

- 예약 서비스 재배포
  kubectl apply -f deployment.yml -n ns-roaming
- 예약서비스에 대한 replica 를 동적으로 늘려주도록 HPA 를 설정한다. 설정은 CPU 사용량이 50프로를 넘어서면 replica 를 10개까지 늘려준다:
  kubectl autoscale deploy reservation --cpu-percent=50 --min=1 --max=10 -n ns-roaming

- 부하(siege) 배포 

kubectl apply -f - <<EOF apiVersion: v1 kind: Pod metadata: name: siege namespace: ns-roaming spec: containers:

image

watch -n 1 kubectl get pod -n ns-roaming

image

무정지 재배포 - (Readiness Probe )

image image

배포기간중 Availability 가 평소 100%에서 39% 대로 떨어지는 것을 확인. 원인은 쿠버네티스가 성급하게 새로 올려진 서비스를 READY 상태로 인식하여 서비스 유입을 진행한 것이기 때문. 이를 막기위해 Readiness Probe 를 설정함:

# deployment.yaml 의 readiness probe 의 설정:

image

kubectl apply -f kubernetes/read_deployment.yaml

추가 작성 - 셀프힐링 Liveness probe

-deployment.yml 파일에 Liveness porbe 추가

image

-부하 발생 siege -c2 -t120S -v --content-type "application/json" 'http://gateway:8080/reservations POST {"reserveId": "5", "amonut": "1"}'

image

-부하 발생 후 모니터링시 restart건수 증가 확인 : watch -n 1 kubectl get pod -n ns-roaming

image

추가작성 - Readiness

yaml파일 수정 및 배포 완료
상태 확인을 위해 소스를 재 빌드함
신규 버전을 배포함

mvn package  

az acr build --registry user1414acr --image user1414acr.azurecr.io/roaming-reservation:latest --file Dockerfile .

kubectl apply -f deployment.yml -n ns-roaming
최종 결과 확인

image

image

배포기간 동안 Availability 가 변화없기 때문에 무정지 재배포가 성공한 것으로 확인됨.