TheKoguryo's 기술 블로그

 Version 2023.11.20

4.4.1 Service Mesh 없는 마이크로서비스 앱 배포

본 내용은 아래 Istio 문서 상에 있는 내용을 재 확인하는 내용으로 마이크로 서비스 앱을 사용하는 데 있어서 어떤 문제가 발생할 수 있는지, 왜 Istio 같은 Service Mesh 필요한지에 대해 알아보는 내용입니다.

테스트 마이크로 서비스 앱(Bookinfo) 배포

테스트 앱

  • Product, Review, Details, Ratings의 4개 마이크로 서비스 앱으로 구성되어 있습니다.

  • 먼저 여기서는 Reviews 서비스는 v1만 배포합니다.

  • 아직 istio 설정이 안된 상태에서 배포 및 결과를 확인합니다.

  1. 앱 배포

    kubectl apply -l version!=v2,version!=v3 -f https://raw.githubusercontent.com/istio/istio/release-1.18/samples/bookinfo/platform/kube/bookinfo.yaml
    
  2. 스케일

    kubectl scale deployments --all --replicas 3
    
  3. 외부 접근을 위한 서비스 오픈

    테스트 편의를 위해 Load Balancer 타입으로 오픈합니다.

    kubectl patch svc productpage -p '{"spec": {"type": "LoadBalancer"}}'
    
  4. 배포 상태 확인

    각 앱이 3개의 Pod이며 각 Pod는 Container 1개로 구성된 것을 확인할 수 있습니다.

    $ kubectl get pod
    NAME                              READY   STATUS    RESTARTS   AGE
    details-v1-6997d94bb9-8hc7b       1/1     Running   0          30s
    details-v1-6997d94bb9-cjsn7       1/1     Running   0          31s
    details-v1-6997d94bb9-sv2p5       1/1     Running   0          30s
    productpage-v1-58b4c9bff8-9x5r4   1/1     Running   0          29s
    productpage-v1-58b4c9bff8-b87xs   1/1     Running   0          28s
    productpage-v1-58b4c9bff8-sjj5h   1/1     Running   0          28s
    ratings-v1-b8f8fcf49-2njx4        1/1     Running   0          28s
    ratings-v1-b8f8fcf49-4x5fj        1/1     Running   0          28s
    ratings-v1-b8f8fcf49-xmfwq        1/1     Running   0          27s
    reviews-v1-5896f547f5-47fmg       1/1     Running   0          27s
    reviews-v1-5896f547f5-8f5dk       1/1     Running   0          27s
    reviews-v1-5896f547f5-hkhtq       1/1     Running   0          27s
    
  5. 서비스 확인

    $ kubectl get svc
    NAME              TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)             AGE
    details           ClusterIP      10.96.74.5      <none>           9080/TCP            17m
    kubernetes        ClusterIP      10.96.0.1       <none>           443/TCP,12250/TCP   4d13h
    productpage       LoadBalancer   10.96.122.193   129.154.xx.xxx   9080:30930/TCP      17m
    ratings           ClusterIP      10.96.88.44     <none>           9080/TCP            17m
    reviews           ClusterIP      10.96.29.37     <none>           9080/TCP            17m
    
  6. 메인 페이지 접속

    • http://{productpage external ip}:9080/productpage
    • 각 서비스가 호출되어 정상적인 페이지가 보이는 것을 알 수 있습니다.
    • Review v1에서는 아직 Rating 서비스와 연동되지 않아 Rating 정보는 보이지 않습니다.

    image-20230705132950220

  7. Product page 호출 코드 확인

    • 소스 파일: https://github.com/istio/istio/blob/release-1.18/samples/bookinfo/src/productpage/productpage.py

    • reviews 호출 주소를 보면 http://{reviewsHostname}.{servicesDomain}:{reviewsPort}/reviews 인걸 알 수 있습니다.

      image-20230705133200875

    • 실제 호출 로그

      실제 로그를 보면 http://reviews:9080/reviews 로 정상 호출 되었으며, 배포시의 ClusterIP 타입의 reviews Service를 통해 같은 네임스페이스 상에서는 reviews를 주소로하여 정상 호출된 것을 알 수 있습니다.

      $ kubectl logs -l app=productpage -f
      ...
      DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): reviews:9080
      send: b'GET /reviews/0 HTTP/1.1\r\nHost: reviews:9080\r\nuser-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nX-B3-TraceId: b4eb54b1b2a1e29d\r\nX-B3-SpanId: b4eb54b1b2a1e29d\r\nX-B3-Sampled: 1\r\n\r\n'
      reply: 'HTTP/1.1 200 OK\r\n'
      header: X-Powered-By: Servlet/3.1
      header: Content-Type: application/json
      header: Date: Wed, 05 Jul 2023 04:32:21 GMT
      header: Content-Language: en-US
      header: Content-Length: 358
      DEBUG:urllib3.connectionpool:http://reviews:9080 "GET /reviews/0 HTTP/1.1" 200 358
      INFO:werkzeug:::ffff:10.244.0.128 - - [05/Jul/2023 04:32:21] "GET /productpage HTTP/1.1" 200 -
      

Reviews 새 버전 배포

  1. Reviews Service 확인

    Selector에서 보이는 것처럼 app=reviews 레이블이 달린 Pod로 분배하고 있습니다. 현재 배포되어있는 Reviews v1의 세 개의 Pod의 Endpoints로 분배되고 있습니다.

    $ kubectl describe svc reviews
    Name:              reviews
    Namespace:         default
    Labels:            app=reviews
                       service=reviews
    Annotations:       <none>
    Selector:          app=reviews
    Type:              ClusterIP
    IP Family Policy:  SingleStack
    IP Families:       IPv4
    IP:                10.96.29.37
    IPs:               10.96.29.37
    Port:              http  9080/TCP
    TargetPort:        9080/TCP
    Endpoints:         10.244.0.151:9080,10.244.0.27:9080,10.244.1.22:9080
    Session Affinity:  None
    Events:            <none>
    
  2. 이 상태에서 Reviews 앱의 새로운 버전인 v2를 개발하여 배포하게 된다면, 아마 배포해서 정상동작 확인후 서비스 라우팅이 되도록 app=review를 추가하게 될 것입니다. 이후 일부 요청을 서비스 하도록 실제 운영하다 문제 발생시 원복시키거나, 문제가 없는 경우, v1로 가는 요청을 줄이고, v2로 가는 요청을 점진적으로 늘려가면서 실제 v2로 이관하게 될 것입니다.

Reviews v2 배포
  1. Reviews v2 버전을 배포합니다.

    라우팅에서 일단 제외하기 위해 app: reviews_test로 레이블을 변경하여 배포합니다.

    curl -s https://raw.githubusercontent.com/istio/istio/release-1.18/samples/bookinfo/platform/kube/bookinfo.yaml | sed 's/app: reviews/app: reviews_test/' | kubectl apply -l app=reviews_test,version=v2 -f -
    
  2. 배포 결과 확인

    $ kubectl get pod -o wide
    NAME                              READY   STATUS    RESTARTS   AGE     IP             NODE          NOMINATED NODE   READINESS GATES
    ...
    reviews-v1-5896f547f5-47fmg       1/1     Running   0          6m51s   10.244.1.22    10.0.10.220   <none>           <none>
    reviews-v1-5896f547f5-8f5dk       1/1     Running   0          6m51s   10.244.0.151   10.0.10.193   <none>           <none>
    reviews-v1-5896f547f5-hkhtq       1/1     Running   0          6m51s   10.244.0.27    10.0.10.41    <none>           <none>
    reviews-v2-869797b54b-2sxjw       1/1     Running   0          22s     10.244.0.152   10.0.10.193   <none>           <none>
    
  3. Reviews v2 Pod 테스트

    productpage Pod에서 Reviews v2 Pod로 테스트하면 좋겠지만, productpage Pod에 테스트용 curl 없는 관계로 별도 클라이언트용 Pod를 배포하여 테스트합니다.

    • 테스트 클라이언트 Pod 배포

      kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.18/samples/sleep/sleep.yaml
      
    • 테스트

      REVIEWS_V2_POD_IP=$(kubectl get pod -l app=reviews_test,version=v2 -o jsonpath='{.items[0].status.podIP}')
      echo $REVIEWS_V2_POD_IP
      kubectl exec $(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}') -- curl -sS "$REVIEWS_V2_POD_IP:9080/reviews/7" 
      
  4. 테스트 결과

    아래와 같이 v2 버전이 잘 동작함을 확인합니다.

    $ REVIEWS_V2_POD_IP=$(kubectl get pod -l app=reviews_test,version=v2 -o jsonpath='{.items[0].status.podIP}')
    $ echo $REVIEWS_V2_POD_IP
    10.244.0.152
    $ kubectl exec $(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}') -- curl -sS "$REVIEWS_V2_POD_IP:9080/reviews/7" 
    {"id": "7","podname": "reviews-v2-869797b54b-2sxjw","clustername": "null","reviews": [{  "reviewer": "Reviewer1",  "text": "An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!", "rating": {"stars": 5, "color": "black"}},{  "reviewer": "Reviewer2",  "text": "Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare.", "rating": {"stars": 4, "color": "black"}}]}
    
Reviews v2의 서비스 라우팅에 추가
  1. version=v2 레이블이 달린 Review v2 Pod에 app=reviews 레이블을 추가합니다.

    kubectl label pod -l version=v2 app=reviews --overwrite
    
  2. Reviews Service 재확인

    아래와 같이 v2가 추가가 되어 Endpoints에 이전 3개에서 4개로 변경된 것을 알 수 있습니다.

    $ kubectl describe svc reviews
    Name:              reviews
    ...
    Selector:          app=reviews
    ...
    Endpoints:         10.244.0.151:9080,10.244.0.152:9080,10.244.0.27:9080 + 1 more...
    ...
    
  3. 메인 페이지 접속

    • http://{productpage external ip}:9080/productpage

    • 페이지를 새로고침하면 4번 중 1번은 Review v2 Pod로 라우팅되어 아래 그림과 같이 별점 표시가 되는 것을 볼 수 있습니다.

      image-20230705133820095

Reviews v2 문제 확인후 재배포
  1. 서비스 중에 문제가 발생한 경우 다음과 같이 삭제합니다.

    • 위에서 서비스 라우팅을 위해 기존 Pod의 label을 변경했기 때문에, 변경된 Pod(app=reviews)외에 app=reviews_test의 Deployment도 아직 남아 있습니다. 둘다 삭제합니다. (실제로는 이렇게 하면 안되겠네요. 근데, Deployment 내에 정의된 Pod Template의 label은 변경이 안되네요…)
    kubectl delete deployment reviews-v2
    kubectl delete pod -l app=reviews,version=v2
    
  2. 문제 해결후 재배포

    최종 배포시에는 아래와 같이 배포후 v2의 갯수를 늘리고 v1은 삭제하게 될 것입니다. 실제 상황에서는 점진적으로 v2을 늘리고 v1을 줄이는 요구사항도 있을 것입니다.

    kubectl apply -l app=reviews,version=v2 -f https://raw.githubusercontent.com/istio/istio/release-1.18/samples/bookinfo/platform/kube/bookinfo.yaml
    kubectl scale deployment reviews-v2 --replicas=3
    kubectl delete deployment reviews-v1
    

실제 배포시 대응 방법

실제 환경에서 마이크로 서비스 업데이트시의 안정적인 배포를 위해 새 버전 배포후 테스트, 점진적인 새 버전으로의 이관을 위한 여러가지 방안들이 나왔습니다. 그 대표적인 방법으로 가장 많이 사용하고 있는 것은 크게 Service Mesh와 Netflix’s Hystrix를 기반한 방법입니다.

  • Service Mesh: Istio로 대표되는 서비스 메쉬를 사용하는 방법은 sidecar로 모듈을 Service Mesh 단에서 추가하는 방법입니다. Pod에 Application Container외에 Sidecar Container가 추가되고, Sidecar를 이용해 서비스 라우팅 및 추가 기능을 제공하는 방식입니다. 애플리케이션 코드에 별도 작업이 필요하지 않는 장점이 있습니다.
  • 애플리케이션 코드 구현: Netflix’s Hystrix 예와 같이 서비스 라우팅, 보안, 모니터링 등에 필요한 기능을 라이브러리 모듈에서 제공하는 기능을 사용하는 방식입니다. 라이브러리를 사용하기 때문 코딩 자체량은 많지 않고, Annotation 등을 사용하게 됩니다. Spring 프레임워크에서는 Spring Cloud로 발전하여 기능을 제공하고 있습니다.


이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.

Last updated on 20 Dec 2021