[Study 7주차] 쿠버네티스 Service Mesh - Istio 기능 & Bookinfo 실습
- -
CoudNet@ 팀의 가시다님께서 리딩하시는 KANS Study (Kubernetes Advanced Networking Study) 7주차 스터디 내용 정리
저번 글에 이어서 서비스 메시 Istio 대해 알아보겠습니다.
이번에는 이스티오 공식 문서에 있는 데모 어플리케이션인 Book Info 를 배포한 후 이스티오의 동작을 이해해보겠습니다.
1. Book Info 어플리케이션
이스티오 실습을 위한 데모 마이크로서비스 어플리케이션입니다.
Productpage, reviews, ratings, details 의 4개의 마이크로서비스로 구성되어 있습니다.

Book info 어플리케이션의 동작방식
1. ProductPage 페이지에서 요청
2. 도서 리뷰를 보여주는 Reviews 서비스와 도서 상세 정보를 보여주는 Details 서비스에 접속
3. ProductPage 는 Reviews 와 Details 결과 응답
4. Reviews 서비스는 v1, v2, v3 세 개의 버전이 있고 v2, v3 버전의 경우 Ratings 서비스에 접속하여 도서에 대한 5단계 평가 응답
5. v1 Review 는 Rating 이 없고, v2 Review 는 검은색 별로 Ratings 표시, v3 Review는 색깔이 있는 별로 Ratings 표시
1.1. Book Info 어플리케이션 배포
실습환경은 이전 글에서 구성한 서버를 이용합니다.
이스티오가 설치된 서버에서 실행합니다.
Book info Yaml 파일은 아래와 같습니다.
# Copyright Istio Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ################################################################################################## # This file defines the services, service accounts, and deployments for the Bookinfo sample. # # To apply all 4 Bookinfo services, their corresponding service accounts, and deployments: # # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml # # Alternatively, you can deploy any resource separately: # # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l service=reviews # reviews Service # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews # reviews ServiceAccount # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v3 # reviews-v3 Deployment ################################################################################################## ################################################################################################## # Details service ################################################################################################## apiVersion: v1 kind: Service metadata: name: details labels: app: details service: details spec: ports: - port: 9080 name: http selector: app: details --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-details labels: account: details --- apiVersion: apps/v1 kind: Deployment metadata: name: details-v1 labels: app: details version: v1 spec: replicas: 1 selector: matchLabels: app: details version: v1 template: metadata: labels: app: details version: v1 spec: serviceAccountName: bookinfo-details containers: - name: details image: docker.io/istio/examples-bookinfo-details-v1:1.20.1 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- ################################################################################################## # Ratings service ################################################################################################## apiVersion: v1 kind: Service metadata: name: ratings labels: app: ratings service: ratings spec: ports: - port: 9080 name: http selector: app: ratings --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-ratings labels: account: ratings --- apiVersion: apps/v1 kind: Deployment metadata: name: ratings-v1 labels: app: ratings version: v1 spec: replicas: 1 selector: matchLabels: app: ratings version: v1 template: metadata: labels: app: ratings version: v1 spec: serviceAccountName: bookinfo-ratings containers: - name: ratings image: docker.io/istio/examples-bookinfo-ratings-v1:1.20.1 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- ################################################################################################## # Reviews service ################################################################################################## apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-reviews labels: account: reviews --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v1 labels: app: reviews version: v1 spec: replicas: 1 selector: matchLabels: app: reviews version: v1 template: metadata: labels: app: reviews version: v1 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v1:1.20.1 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v2 labels: app: reviews version: v2 spec: replicas: 1 selector: matchLabels: app: reviews version: v2 template: metadata: labels: app: reviews version: v2 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v2:1.20.1 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v3 labels: app: reviews version: v3 spec: replicas: 1 selector: matchLabels: app: reviews version: v3 template: metadata: labels: app: reviews version: v3 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v3:1.20.1 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} --- ################################################################################################## # Productpage services ################################################################################################## apiVersion: v1 kind: Service metadata: name: productpage labels: app: productpage service: productpage spec: ports: - port: 9080 name: http selector: app: productpage --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-productpage labels: account: productpage --- apiVersion: apps/v1 kind: Deployment metadata: name: productpage-v1 labels: app: productpage version: v1 spec: replicas: 1 selector: matchLabels: app: productpage version: v1 template: metadata: annotations: prometheus.io/scrape: "true" prometheus.io/port: "9080" prometheus.io/path: "/metrics" labels: app: productpage version: v1 spec: serviceAccountName: bookinfo-productpage containers: - name: productpage image: docker.io/istio/examples-bookinfo-productpage-v1:1.20.1 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp volumes: - name: tmp emptyDir: {} ---
# Book info 설치 ISTIOV=1.23.2 echo $ISTIOV kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml # 해당 서버에서 접속 테스트 kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"


1.2. Book Info 어플리케이션 접속 구성 및 확인
Istio Gateway & VirtualService 배포
- bookinfo-gateway.yaml 정보
파드에 진입하기 위한 게이트웨이 및 라우터 설정입니다.
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: bookinfo-gateway spec: # The selector matches the ingress gateway pod labels. # If you installed Istio using Helm following the standard documentation, this would be "istio=ingress" selector: istio: ingressgateway # use istio default controller servers: - port: number: 8080 name: http protocol: HTTP hosts: - "*" --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "*" gateways: - bookinfo-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080
# Gateway & VirtualService 배포 kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/networking/bookinfo-gateway.yaml # Gateway & VirtualService 정보 확인 kubectl get gw,vs istioctl proxy-status

외부 접속 테스트
- k3s-s 서버에서 접속
export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}') # Control Plane curl -s http://localhost:$IGWHTTP/productpage # Node 1번 curl -s http://192.168.10.101:$IGWHTTP/productpage # Node 2번 curl -s http://192.168.10.102:$IGWHTTP/productpage
- Local PC 에서 접속
curl -v -s $MYDOMAIN:$IGWHTTP/productpage

- testpc 에서 접속
curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>"

브라우저를 통한 접근 확인
echo -e "http://$MYDOMAIN:$IGWHTTP/productpage" # http://www.kimalarm.com:32251/productpage

2. 이스티오 모니터링
Kiali 를 통해 이스티오를 연동하여 시각화 대시보드를 구성할 수 있습니다.
저희는 데모 환경이므로, kiali / Prometheus / Grafana / Jaeger 를 한 번에 설치했습니다.
2.1. 모니터링 도구 구성
# 샘플 애드온 디렉토리 구조 확인 tree ~/istio-$ISTIOV/samples/addons/ # 출력 화면 /root/istio-1.23.2/samples/addons/ ├── README.md ├── extras │ ├── prometheus-operator.yaml │ ├── skywalking.yaml │ └── zipkin.yaml ├── grafana.yaml ├── jaeger.yaml ├── kiali.yaml ├── loki.yaml └── prometheus.yaml
# 도구 설치 kubectl apply -f ~/istio-$ISTIOV/samples/
각 도구별 노드포트 구성
편의를 위해 노드포트로 접근을 허용해줍니다.
# Kiali 서비스 kubectl patch svc -n istio-system kiali -p '{"spec":{"type":"NodePort"}}' KIALINodePort=$(kubectl get svc -n istio-system kiali -o jsonpath={.spec.ports[0].nodePort}) echo -e "KIALI UI URL = http://$(curl -s ipinfo.io/ip):$KIALINodePort"

# Grafana 서비스 kubectl patch svc -n istio-system grafana -p '{"spec":{"type":"NodePort"}}' GRAFANANodePort=$(kubectl get svc -n istio-system grafana -o jsonpath={.spec.ports[0].nodePort}) echo -e "Grafana URL = http://$(curl -s ipinfo.io/ip):$GRAFANANodePort"

# Prometheus 서비스 kubectl patch svc -n istio-system prometheus -p '{"spec":{"type":"NodePort"}}' PROMENodePort=$(kubectl get svc -n istio-system prometheus -o jsonpath={.spec.ports[0].nodePort}) echo -e "Prometheus URL = http://$(curl -s ipinfo.io/ip):$PROMENodePort"

이스티오와 엔보이 메트릭이 수집되고 있는 것을 알 수 있습니다.


2.2. Kiali 대시보드 둘러보기
방금 구성한 Kiali 에서 시각화 대시보드를 확인해봅니다.
혹시 아무런 그래프가 그려지지 않으면, 우측 상단에 시간을 늘려주거나 서버에 트래픽을 흘려보내면 됩니다.
# testpc 서버에서 진행 while true; do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; echo "--------------" ; sleep 1; don

Security 정보 확인
Security 탭을 누르면 우측 패널에 인증에 대한 정보가 나옵니다.

3. Traffic Management
서비스 메시 이스티오의 다양한 네트워크 제어 기능을 살펴보겠습니다.
3.1. Request Routing
Request Routing 은 의도한 바로 네트워크 라우팅을 설정할 수 있는 기능입니다.
# 기본 Destination Rule 적용 cd ~/istio-$ISTIOV/samples/bookinfo/networking tree # 기본 DestinationRule 적용 kubectl apply -f destination-rule-all.yaml

reviews v1 으로만 통신 설정
해당 VirtualService Yaml 내용입니다.
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: productpage spec: hosts: - productpage http: - route: - destination: host: productpage subset: v1 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - route: - destination: host: ratings subset: v1 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: details spec: hosts: - details http: - route: - destination: host: details subset: v1 ---
# istio vs(virtualservices) 확인 kubectl get vs NAME GATEWAYS HOSTS AGE bookinfo ["bookinfo-gateway"] ["*"] 44m # 모든 마이크로서비스에 대해 v1 의 서브셋(subset) 에 전송되게 virtualservices 적용 kubectl apply -f virtual-service-all-v1.yaml
해당 설정 적용 후, reviews 서비스에 대해 v1 로만 트래픽이 흐르는 것을 알 수 있습니다.

reviews v2 으로만 통신 설정
match 의 exact 를 사용해 값이 완전히 일치되는 값만 별도의 destination 라우팅을 설정할 수 있습니다.
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - match: - headers: end-user: exact: jason route: - destination: host: reviews subset: v2 - route: - destination: host: reviews subset: v1
jason 유저에 대해서는 v2 로 이동하게 설정합니다.
kubectl apply -f virtual-service-reviews-test-v2.yaml
Book Info 웹페이지에서 jason 으로 로그인 합니다. (비밀번호는 아무거나 입력해도 로그인 가능)
jason 로그인 성공 시, reviews v2 에 해당하는 검은 별 리뷰 화면이 출력됩니다.

Kiali 대시보드에서도 v2 로 흐르는 트래픽이 캡쳐됩니다.

3.2. Fault Injection
Fault Injection 은 의도적으로 서비스를 지연 시키거나 중단하는 것입니다.
jason 유저는 7초 지연 발생, 그외 사용자는 정상 연결
jason 유저에 대해 7초의 지연 시간을 발생 시키는 설정입니다.
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - match: - headers: end-user: exact: jason fault: delay: percentage: value: 100.0 fixedDelay: 7s route: - destination: host: ratings subset: v1 - route: - destination: host: ratings subset: v1
kubectl apply -f virtual-service-ratings-test-delay.yaml
jason 유저로 로그인을 시도해봅니다.
7초 이상 로그인이 지연되었기 때문에, 실패 응답을 반환합니다.

kiali 대시보드에서 해당 트래픽에 문제가 있는 것을 알 수 있습니다.

jason 유저는 500 에러 발생, 그외 사용자는 정상 연결
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - match: - headers: end-user: exact: jason fault: abort: percentage: value: 100.0 httpStatus: 500 route: - destination: host: ratings subset: v1 - route: - destination: host: ratings subset: v1
kubectl apply -f virtual-service-ratings-test-abort.yaml
jason 유저로 로그인 시도 시, reviews 서비스에 오류가 발생한 것을 확인할 수 있습니다.

4. Istio 보안
보안이 강한 회사에서는 제로 트러스트 정책을 기반으로 보안 거버넌스를 수립할 것입니다.
제로 트러스트는 아무도 신뢰할 수 없으며,
사내 내부자들 또한 악의적인 공격을 할 수 있다는 가정하에 최소한의 권한과 세밀한 통제를 지속적으로 수행하는 보안 활동을 의미합니다.
4.1. mTLS Authentication
Istio 는 기본적으로 mtls (상호 인증) 방식의 통신을 하기 때문에 보안 효율성을 높일 수 있습니다.
* TLS
- 암호화를 통한 인증, 통신 기밀성 제공
* mTLS
- 서버측도 클라이언트측에 대한 인증서 확인 및 액세스 권한 확인

Authentication 방식
* Permissive Mode
- 일반 텍스트 트래픽과 상호 TLS 트래픽을 동시에 허용
* Strict Mode
- 상호 TLS 트래픽만 허용
4.2. Authorization
서버 측 Envoy 프록시에서 인바운드 트래픽에 대한 액세스 제어가 가능합니다.
AuthorizationPolicy 리소스를 통해 권한을 부여(Allow) 하거나 거절(Deny) 할 수 있습니다.
Authorization 방식
- Implicit enablement : 인증 미적용 시 모든 요청 허용
- AuthorizationPolicy 는 허용(Allow), 거부(Deny), 사용자 지정(Custom) 등의 동작
- 정책 적용 우선 순위는 Custom - Deny - Allow 순으로 적용
5. Istio 트래픽 흐름
이렇게 다양한 기능을 제공하고 편리한데, 이스티오 도입을 주저하게 만드는 이유는 바로 트래픽 성능 저하 때문일 것입니다.
이전 글의 첫 단락에서도 다뤘지만 서비스 메시가 없는 환경에서 어플리케이션 간 통신은 파드와 파드가 Direct 통신입니다.
서비스 메시가 생기게 되면서 Proxy 가 해당 네트워크 패킷을 가로채 통신을 하게 되는데, 이 때문에 네트워크 지연이 발생합니다.

해당 아키텍처는 외부 트래픽이 파드로 들어왔을 때 트래픽의 흐름을 보여주는 그림입니다.
참고해야할 점은 저 경로를 왕복으로 지나가야 실제 엔드유저가 응답을 받을 수 있다는 것입니다.
그렇기 때문에 단순한 서비스 구조라면 Istio 가 꼭 필요한 지 한 번 생각해봐야될 것입니다.
이번에 서비스 메시를 공부하면서 이스티오의 동작 방식에 대해 많은 공부를 할 수 있었습니다.
양이 방대하여 아직 부족한 부분이 너무 많은데, 시간 날 때마다 틈틈히 공부해야될 것 같습니다.