4.4.1 Service Mesh 없는 마이크로서비스 앱 배포
본 내용은 아래 Istio 문서 상에 있는 내용을 재 확인하는 내용으로 마이크로 서비스 앱을 사용하는 데 있어서 어떤 문제가 발생할 수 있는지, 왜 Istio 같은 Service Mesh 필요한지에 대해 알아보는 내용입니다.
- https://istio.io/latest/docs/examples/microservices-istio/bookinfo-kubernetes/
- https://istio.io/latest/docs/examples/microservices-istio/add-new-microservice-version/
테스트 마이크로 서비스 앱(Bookinfo) 배포
테스트 앱
Product, Review, Details, Ratings의 4개 마이크로 서비스 앱으로 구성되어 있습니다.
먼저 여기서는 Reviews 서비스는 v1만 배포합니다.
아직 istio 설정이 안된 상태에서 배포 및 결과를 확인합니다.
앱 배포
kubectl apply -l version!=v2,version!=v3 -f https://raw.githubusercontent.com/istio/istio/release-1.18/samples/bookinfo/platform/kube/bookinfo.yaml
스케일
kubectl scale deployments --all --replicas 3
외부 접근을 위한 서비스 오픈
테스트 편의를 위해 Load Balancer 타입으로 오픈합니다.
kubectl patch svc productpage -p '{"spec": {"type": "LoadBalancer"}}'
배포 상태 확인
각 앱이 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
서비스 확인
$ 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
메인 페이지 접속
- http://{productpage external ip}:9080/productpage
- 각 서비스가 호출되어 정상적인 페이지가 보이는 것을 알 수 있습니다.
- Review v1에서는 아직 Rating 서비스와 연동되지 않아 Rating 정보는 보이지 않습니다.
Product page 호출 코드 확인
소스 파일: https://github.com/istio/istio/blob/release-1.18/samples/bookinfo/src/productpage/productpage.py
reviews 호출 주소를 보면 http://{reviewsHostname}.{servicesDomain}:{reviewsPort}/reviews 인걸 알 수 있습니다.
실제 호출 로그
실제 로그를 보면 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 새 버전 배포
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>
이 상태에서 Reviews 앱의 새로운 버전인 v2를 개발하여 배포하게 된다면, 아마 배포해서 정상동작 확인후 서비스 라우팅이 되도록 app=review를 추가하게 될 것입니다. 이후 일부 요청을 서비스 하도록 실제 운영하다 문제 발생시 원복시키거나, 문제가 없는 경우, v1로 가는 요청을 줄이고, v2로 가는 요청을 점진적으로 늘려가면서 실제 v2로 이관하게 될 것입니다.
Reviews v2 배포
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 -
배포 결과 확인
$ 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>
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"
테스트 결과
아래와 같이 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의 서비스 라우팅에 추가
version=v2 레이블이 달린 Review v2 Pod에 app=reviews 레이블을 추가합니다.
kubectl label pod -l version=v2 app=reviews --overwrite
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... ...
메인 페이지 접속
http://{productpage external ip}:9080/productpage
페이지를 새로고침하면 4번 중 1번은 Review v2 Pod로 라우팅되어 아래 그림과 같이 별점 표시가 되는 것을 볼 수 있습니다.
Reviews v2 문제 확인후 재배포
서비스 중에 문제가 발생한 경우 다음과 같이 삭제합니다.
- 위에서 서비스 라우팅을 위해 기존 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
문제 해결후 재배포
최종 배포시에는 아래와 같이 배포후 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로 발전하여 기능을 제공하고 있습니다.
이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.