본문 바로가기

컨테이너/쿠버네티스 네트워크

[KANS] 쿠버네티스 네트워크 (13) Service Mesh : istio

목표:

1. 서비스 메시 소개

2. Envoy 소개

3. istio 소개 

4. istio 기본 default 설정 (gateway , virtual service) 를 통한 기본 인입

서비스 메시

 

등장배경 : 

마이크로서비스 아키텍처 환경의 시스템 전체 모니터링의 어려움, 운영 시 시스템 장애나 문제 발생할 때 원인과 병목 구간 찾기 어려움

내부망 진입점에 역할을 하는 GW(예. API Gateway) 경우 모든 동작 처리에 무거워지거나, 내부망 내부 통신 제어는 어려움

 

기본 동작 :

파드 간 통신 경로에 프록시를 놓고 트래픽 모니터링이나 트래픽 컨트롤 → 기존 애플리케이션 코드에 수정 없이 구성 가능!

1. 애플리케이션의 변경 없이 , 모든 애플리케이션 통신 사이에 Proxy를 두고 통신 해보자.

  • 파드 내에 사이드 카 컨테이너로 주입 
  • Proxy 컨테이너가 Application 트래픽을 가로채야됨 ( iptables rule 구현)

2. Proxy는 결국 Dataplane이니, 이를 중앙에서 관리하는 ControlPlane을 두고 중앙에서 관리

  • Proxy는 중앙에서 설정관리가 잘되는 툴을 선택, 즉 원격에서 동적인 설정관리가 유연해야함 
  • 중앙에서 어떤 동작/설정을 관리해야 할까? 라우팅 , 보안 통신을 위한 mtLS 관련, 동기화 상태 정보 등

특징: 

Observablity

  • 에러율, 레이턴시 , 커넥션 개수, 요청 개수 등
  • 메트릭 모니터링 , 특정 서비스 간 혹은 특정 요청 경로로 필터링 가능 

Traffic Control

  • 트래픽 시프팅 - 카나리 배포
  • 서킷 브레이커 - 연쇄 장애, 시스템 전체 장애 예방
  • 폴트 인젝션 - 의도적으로 요청을 지연 및 실패 구현
  • 속도 제한 - 요청 개수를 제한

Envoy

 

Envoy

    • L4/7 Proxy, istio의 Sidecar proxy로 사용

  • 서비스메시 솔루션이나 Gatway API 구현체들을 Envoy를 내부적으로 사용하고 있으며, Envoy가 제공하는 동적 구성을 위한 API(xDS Sync API)를 이용하여 다양한 네트워크 정책을 구성 하게 됩니다.

 

 

  • Envoy의 xDS Sync API는 아래와 같은 레이어에서 동작하게 됩니다.
    • LDS - Listener Discovery Service
    • RDS - Route Discovery Service
    • CDS - Cluseter Discovery Service
    • EDS - Endpoint Discovery Service
  •  

실습에서 계속 xDS에 대한 언급이 있으므로 주요 구성요소를 조금 더 살펴보겠습니다,

  • Configuration (좌측):
    • 여러 소스에서 수집된 설정 데이터입니다. 이는 다양한 네트워크, 보안, 라우팅, 엔드포인트 정보를 포함할 수 있습니다. 컨트롤 플레인은 이러한 설정 데이터를 모아서 Envoy에 전달합니다.
  • Control Plane (중앙):
    • Envoy가 사용할 설정 정보를 수집하고 관리하는 역할을 합니다. 컨트롤 플레인은 xDS API를 통해 Envoy에게 다양한 설정 데이터를 제공합니다. 여기서 각 API는 서로 다른 유형의 설정을 제공하며, 이를 통해 Envoy가 트래픽을 효과적으로 제어할 수 있습니다.
  • xDS APIs (우측):
    • 컨트롤 플레인은 다양한 xDS API를 통해 Envoy로 데이터를 전송하며, 각 API의 역할은 다음과 같습니다
    • SDS (Secret Discovery Service): Envoy가 TLS 인증서와 같은 보안 정보를 동적으로 가져오는 서비스
    • EDS (Endpoint Discovery Service): 백엔드 서비스의 엔드포인트 정보를 Envoy에게 제공
    • CDS (Cluster Discovery Service): 클러스터 정보를 전달합니다. 클러스터는 백엔드 서버의 그룹으로, Envoy는 이를 통해 어떤 서버로 트래픽을 보낼지 결정
    • LDS (Listener Discovery Service): Envoy의 리스너 설정을 관리합니다. 리스너는 네트워크 요청을 수신하는 엔티티
    • RDS (Route Discovery Service): 트래픽 라우팅 규칙을 제공합니다. Envoy는 이를 통해 클라이언트 요청을 적절한 백엔드 서비스로 라우팅
  • GRPC (Envoy컨테이너와의 통신):
    • 앞서 설명했듯이 컨트롤 플레인은 Envoy와 gRPC를 통해 통신합니다. gRPC는 고성능 원격 프로시저 호출(Remote Procedure Call) 시스템으로, Envoy는 이 프로토콜을 통해 컨트롤 플레인으로부터 동적으로 설정을 받아 트래픽을 제어

 

 

Envoy 실습

  • envoy install
# 설치
# echo "deb [signed-by=/etc/apt/keyrings/envoy-keyring.gpg] https://apt.envoyproxy.io focal main" | sudo tee /etc/apt/sources.list.d/envoy.list
wget -O- https://apt.envoyproxy.io/signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/envoy-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/envoy-keyring.gpg] https://apt.envoyproxy.io jammy main" | sudo tee /etc/apt/sources.list.d/envoy.list
sudo apt-get update && sudo apt-get install envoy -y

# 확인
envoy --version

# 도움말
envoy --help
  • envoy test
# (터미널1) 데모 config 적용하여 실행
curl -O https://www.envoyproxy.io/docs/envoy/latest/_downloads/92dcb9714fb6bc288d042029b34c0de4/envoy-demo.yaml
envoy -c envoy-demo.yaml


# (터미널2) 정보 확인
ss -tnlp
State    Recv-Q  Send-Q    Local Address:Port   Peer Address:Port    Process
LISTEN   0       4096      0.0.0.0:10000        0.0.0.0:*            users:(("envoy",pid=8007,fd=18),("envoy",pid=8007,fd=16))

# 접속 테스트
curl -s http://127.0.0.1:10000 | grep -o "<title>.*</title>"

# 외부 접속 정보 출력
echo -e "http://$(curl -s ipinfo.io/ip):10000"
http://[외부아이피]:10000

--------------------
# 자신의 PC 웹브라우저에서 외부 접속 정보 접속 확인!

# k3s-s 에서 접속 테스트
curl -s http://192.168.10.200:10000 | grep -o "<title>.*</title>" 
--------------------

# 연결 정보 확인
ss -tnp

# (터미널1) envoy 실행 취소(CTRL+C) 후 (관리자페이지) 설정 덮어쓰기 - 링크
cat <<EOT> envoy-override.yaml
admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9902
EOT
envoy -c envoy-demo.yaml --config-yaml "$(cat envoy-override.yaml)"

# envoy 관리페이지 외부 접속 정보 출력
echo -e "http://$(curl -s ipinfo.io/ip):9902"
http://[외부아이피]:9902

--------------------
# 자신의 PC 웹브라우저에서 관리 페이지 외부 접속 정보 접속 확인!

istio

1. istio install

# istioctl 설치
export ISTIOV=1.23.2
echo "export ISTIOV=1.23.2" >> /etc/profile
curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV TARGET_ARCH=x86_64 sh -
tree istio-$ISTIOV -L 2 # sample yaml 포함
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
istioctl version --remote=false

# (default 프로파일) 컨트롤 플레인 배포 - 링크 Customizing
# The istioctl command supports the full IstioOperator API via command-line options for individual settings or for passing a yaml file containing an IstioOperator custom resource (CR).
istioctl profile list
istioctl profile dump default
istioctl profile dump --config-path components.ingressGateways
istioctl profile dump --config-path values.gateways.istio-ingressgateway
istioctl profile dump demo

istioctl profile dump demo > demo-profile.yaml
vi demo-profile.yaml # 복잡성을 줄이게 실습 시나리오 환경 맞춤
--------------------
    egressGateways:
    - enabled: false
--------------------    

istioctl install -f demo-profile.yaml -y

        |\
        | \
        |  \
        |   \
      /||    \
     / ||     \
    /  ||      \
   /   ||       \
  /    ||        \
 /     ||         \
/______||__________\
____________________
  \__       _____/
     \_____/

✔ Istio core installed ⛵️
✔ Istiod installed 🧠
✔ Istio core installed ⛵️                                                                                                          
✔ Istiod installed 🧠                                                                                                              
✔ Ingress gateways installed 🛬                                                                                                    
✔ Installation complete                                                                                                            

# 설치 확인 : istiod, istio-ingressgateway
kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
kubectl get crd | grep istio.io | sort

# istio-ingressgateway 의 envoy 버전 확인
kubectl exec -it deploy/istio-ingressgateway -n istio-system -c istio-proxy -- envoy --version
envoy  version: 6c72b2179f5a58988b920a55b0be8346de3f7b35/1.31.2-dev/Clean/RELEASE/BoringSSL

# istio-ingressgateway 서비스 NodePort로 변경
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"type":"NodePort"}}'

# istio-ingressgateway 서비스 확인
kubectl get svc,ep -n istio-system istio-ingressgateway

## istio-ingressgateway 서비스 포트 정보 확인
kubectl get svc -n istio-system istio-ingressgateway -o jsonpath={.spec.ports[*]} | jq

## istio-ingressgateway 디플로이먼트 파드의 포트 정보 확인 
kubectl get deploy/istio-ingressgateway -n istio-system -o jsonpath={.spec.template.spec.containers[0].ports[*]} | jq
kubectl get deploy/istio-ingressgateway -n istio-system -o jsonpath={.spec.template.spec.containers[0].readinessProbe} | jq

# istiod(컨트롤플레인) 디플로이먼트 정보 확인
kubectl exec -it deployment.apps/istiod -n istio-system -- ss -tnlp
kubectl exec -it deployment.apps/istiod -n istio-system -- ss -tnp
kubectl exec -it deployment.apps/istiod -n istio-system -- ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
istio-p+       1       0  0 05:27 ?        00:00:07 /usr/local/bin/pilot-discovery discovery --monitoringAddr=:15014 --log_output_l

# istio-ingressgateway 디플로이먼트 정보 확인
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -tnlp
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -tnp
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ps -ef
istio-p+       1       0  0 05:27 ?        00:00:01 /usr/local/bin/pilot-agent proxy router --domain istio-system.svc.cluster.local
istio-p+      15       1  0 05:27 ?        00:00:11 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drai

kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- cat /etc/istio/proxy/envoy-rev.json
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -xnlp
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -xnp

 

 

  • 사이드카 주입 : Auto Injection with namespace label : 해당 네임스페이스에 생성되는 모든 파드들은 istio 사이드카가 자동으로 injection 됨
(⎈|default:N/A) root@k3s-s:~# kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"type":"NodePort"}}'
service/istio-ingressgateway patched
(⎈|default:N/A) root@k3s-s:~# kubectl label namespace default istio-injection=enabled
namespace/default labeled
(⎈|default:N/A) root@k3s-s:~# kubectl get ns -L istio-injection
NAME              STATUS   AGE    ISTIO-INJECTION
default           Active   2d8h   enabled
istio-system      Active   92s
kube-node-lease   Active   2d8h
kube-public       Active   2d8h
kube-system       Active   2d8h

2. Istio 접속 테스트를 위한 변수 지정

k3s-s )

mypc)
## istio-ingressgateway 파드가 배치된 노드의 유동 공인 IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text
k3s-s	3.38.151.222	running
k3s-w1	15.165.75.117	running
k3s-w2	3.39.223.99	running
testpc	54.180.243.135	running


k3s-s)
# istio ingress gw NodePort(HTTP 접속용) 변수 지정 : 아래 ports[0] 은 어떤 용도의 포트일까요?
export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
echo $IGWHTTP
IGWHTTP=<각자 자신의 NodePort>

# /etc/hosts 파일 수정
MYDOMAIN=<각자 자신의 www 도메인> # 단, 사용하고 있지 않는 공인 도메인을 사용 할 것
MYDOMAIN=www.gasida.dev
echo "<istio-ingressgateway 파드가 있는 워커 노드> $MYDOMAIN" >> /etc/hosts

MYDOMAIN=<각자 자신의 www 도메인>
export MYDOMAIN=www.gasida.dev
echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile

# istio ingress gw 접속 테스트 : 아직은 설정이 없어서 접속 실패가 된다
curl -v -s $MYDOMAIN:$IGWHTTP

 

testpc )

# 아래 변수는 각자 자신의 값을 직접 입력 할 것
IGWHTTP=<각자 출력된 NodePort>
IGWHTTP=32759
export MYDOMAIN=www.gasida.dev
echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile

# istio ingress gw 접속 테스트 : 아직은 설정이 없어서 접속 실패가 된다
curl -v -s $MYDOMAIN:$IGWHTTP

 

curl -v -s $MYDOMAIN:$IGWHTTP

3. Istio 통한 외부 노출

# 로그 모니터링
kubectl get pod -n istio-system -l app=istiod
kubetail -n istio-system -l app=istiod -f

kubectl get pod -n istio-system -l app=istio-ingressgateway
kubetail -n istio-system -l app=istio-ingressgateway -f

 

 

https://devlos.tistory.com/100

  • 파드와 서비스 배포 
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-websrv
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: deploy-websrv
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
spec:
  ports:
    - name: svc-webport
      port: 80
      targetPort: 80
  selector:
    app: deploy-websrv
  type: ClusterIP
EOF

# 사이드카 컨테이너 배포 확인
kubectl get pod,svc,ep -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
deploy-websrv-7d7cf8586c-rhhv8   2/2     Running   0          29s   172.16.2.6   k3s-w2   <none>           <none>
...

kc describe pod
  • gw/vs/ 배포

cat <<EOF | kubectl create -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: test-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: nginx-service
spec:
  hosts:
  - "$MYDOMAIN"
  gateways:
  - test-gateway
  http:
  - route:
    - destination:
        host: svc-clusterip
        port:
          number: 80
EOF

(⎈|default:N/A) root@k3s-s:~# kubectl get gw,vs
NAME                                       AGE
gateway.networking.istio.io/test-gateway   82s

NAME                                               GATEWAYS           HOSTS             AGE
virtualservice.networking.istio.io/nginx-service   ["test-gateway"]   ["www.koo.dev"]   82s

4. istio Book info 실습

Kubernetes 클러스터 내에서 실행되고 있는 ratings 애플리케이션의 Pod에서 (아직 외부 호출 아 님)

 productpage 서비스에 요청 을 보내고 그 응답에서 <title> 태그를 추출

  • 배포 후 모니터링
# 모니터링
watch -d 'kubectl get pod -owide;echo;kubectl get svc'

# Bookinfo 애플리케이션 배포
echo $ISTIOV
cat ~/istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml
kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml

# 확인
kubectl get all,sa

# product 웹 접속 확인
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>"

# 로그
kubetail -l app=productpage -f
  • 초록 글씨 - istio proxy / 빨간 글씨 - productpage
    • Istio의 VirtualService 리소스를 정의
      • 앞단의 istio-proxy에 대한 로그가 확인됩니다.
      • 사용자가 클러스터 내부 pod에서 호출요청을 할 수 있음.
      • http://<gateway-ip>:8080/productpage에 접근하면 productpage 서비스의 포트 9080으로 요청이 전달됩니다.
      • review 1, 2에 대한 페이징이 랜덤으로 전달됩니다.

  • 이제 ingress-gateway를 통한 클러스터 외부에서의 인입을 생성하겠다.
  • 그러기 위해선 gateway와  virtual service가 필요하다.
(⎈|default:N/A) root@k3s-s:~# cat ~/istio-$ISTIOV/samples/bookinfo/networking/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
(⎈|default:N/A) root@k3s-s:~# kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/networking/bookinfo-gateway.yaml
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created
  • k8s-s에서 통신 테스트
# k8s-s에 접속해서 실행
export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
echo $IGWHTTP
32759

# 접속 확인
kubectl get svc -n istio-system istio-ingressgateway
curl -s http://localhost:$IGWHTTP/productpage
curl -s http://192.168.10.101:$IGWHTTP/productpage
curl -s http://192.168.10.102:$IGWHTTP/productpage

# 정보 확인
echo $MYDOMAIN
cat /etc/hosts

#
curl -s http://$MYDOMAIN:$IGWHTTP/productpage
  • 내 노트북에서 통신 테스트
# 노트북에서 실행
echo $MYDOMAIN $IGWHTTP
cat /etc/hosts

#
curl -v -s $MYDOMAIN:$IGWHTTP/productpage
echo -e "http://$MYDOMAIN:$IGWHTTP/productpage"

#
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text