Post

Istio 살펴보기

Istio 살펴보기

💡 KANS 스터디
CloudNet에서 주관하는 KANS(Kubernetes Advanced Networking Study)으로 쿠버네티스 네트워킹 스터디입니다. 아래의 글은 스터디의 내용을 기반으로 작성했습니다.

스터디에 관심이 있으신 분은 CloudNet Blog를 참고해주세요.

들어가며

이번 스터디 주차는 서비스 매시인 Istio이다. 아직 서비스 매시를 다뤄본적이 없는데, 입문부터 좋은 자료로 정리하게 됐다.

Service Mesh

마이크로 서비스간의 통신을 도와주는 별도의 인프라 Layer이다. 전체적인 네트워크 모니터링이 가능하며, 네트워크를 제어할 수 있다. 하지만, 서비스 매시를 도입하면 Layer가 하나 더 추가되므로 무겁다. (아래에 사진만 봐도..) 그렇기에 서비스 매시를 도입하기전에 현재 환경에 정말 필요한지 고민해봐야 한다. 관련 SDS 블로그

image.png

출처: https://www.redhat.com/ko/topics/microservices/what-is-a-service-mesh

Istio

Istio는 서비스 Mesh 구현체 중 하나로, 마이크로서비스 전반의 트래픽 흐름을 관리, 정책 설정, 데이터 수집이 가능하다. 모드로는 파드 당 하나의 프록시를 두는 sidecar모드와 노드 당 전용 커널을 두는 Ambient가 있다. 여기서는 sidecar 모드에 대해서 주로 알아본다.

구성

Istio는 Control Plane과 Data Plane으로 구성된다.

Control Plane은 Istiod로 프록시 라우팅 규칙을 관리, 보안 및 암호화, Kubernetes와 상호 작용한다.

Data Plane은 실제 애플리케이션 바로 앞에 있는 프록시 집합을 의미하며, Sidecar 모드에서는 각 파드마다 sidecar proxy(envoy)가 존재한다.

Control Plane

Istiod는 Pilot, Galley, Citadel 3개의 요소가 존재한다. Pilot은 프록시 라우팅 규칙을 관리하며, Galley는 Endpoint 갱신 등 K8s와 상호작용하며, Citadel은 보안 및 암호화 관련 기능을 담당한다. “Istiod는 golang으로 작성되었다.”

아래의 그림을 통해 각 구성요소의 역할을 확인할 수 있다.

https://istio.io/latest/docs/concepts/security/

https://devlos.tistory.com/100

스터디원이신 데브로스님이 각 구성요소에 대해 도식화를 해주셨다.

Data Plane

Data Plane은 Envoy 프록시 집합을 의미한다.

https://istio.io/latest/docs/ops/deployment/architecture/

Envoy는 멀티 스레딩을 사용하며, 각 서비스에 대해 Listener를 통해 요청을 수신하며 이후 HTTP Router Filer를 통해 대상 클러스터로 옮기고 클러스터에서 실제 엔드포인트(Pod)로 라우팅한다. “Listeners > Routes > Clusters > Endpoints”

https://www.envoyproxy.io/docs/envoy/latest/intro/life_of_a_request

통신흐름

특정 파드로 향하는 트래픽을 어떻게 envoy가 제어할 수 있을까? istio는 pod 수준에서 iptables를 사용하여, 파드로 향하는 트래픽을 가로챈다. 파드내의 컨테이너는 모든 네트워크 스택을 공유하므로 이것이 가능하다. 하지만 이미 해당 파드에 오기까지 Iptables 혹은 CNI에 맞게 여러 네트워크 홉을 거쳐왔지만, 또 다시 Iptables 지옥에 빠지는 느낌이 든다.

image.png

자세한 Kernel Space의 흐름도는 jimmysong님의 블로그에 잘정리되어있다.

Ambient mode 모드 아키텍처

Ambient 모드는 각 파드마다 전용 Proxy 사이드카를 붙이는 방식이 아닌, 하나의 노드에 하나의 ztunnel을 두어 서비스 매시를 구현하는 방식이다. Ambient 모드에서는 L4 Proxy와 L7 Proxy 기능을 분리한다.

image.png

출처: https://istio.io/latest/docs/ambient/architecture/control-plane/

image.png

출처: https://istio.io/latest/blog/2022/introducing-ambient-mesh/

위에는 Sidecar 방식으로 사이드 카 방식과 비교하면 아래와 같이 ztunnel을 통해 통신하는 차이를 확인할 수 있다.

image.png

출처: https://istio.io/latest/docs/ambient/architecture/data-plane/

HBONE(HTTP-Based Overlay Network)은 Istio 구성 요소 간에 사용되는 보안 터널링 프로토콜이다.

만약, 아래와 같은 네트워크 정책이 있었더라면 Ambient 모드 적용시 두번째 네트워크 정책으로 변경되어야 한다. HBONE이기에 8080 포트와 HBONE 트래픽이 지나가는 포트를 열어줘야한다.

(1) Before

1
2
3
4
5
6
7
8
9
10
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
  ingress:
  - ports:
    - port: 9090 # 기존에는 9090만 열어줌
      protocol: TCP
  podSelector:
    matchLabels:
      app.kubernetes.io/name: my-app

(2) After

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
  ingress:
  - ports:
    - port: 8080 # HTTP 기반이기에 8080
      protocol: TCP
    - port: 15008 # HBONE 트래픽을 열어주기 위한 포트
      protocol: TCP
  podSelector:
    matchLabels:
      app.kubernetes.io/name: my-app

실습

실습 환경

실습 환경은 Kind(controlplane 1대) + istio-ingressgateway(NodePort)로 구성되었으며 Istio 버전은 v1.23.2이다.

설치 진행

  • Istioctl을 통해 설치 진행
1
2
3
4
export ISTIOV=1.23.2
curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
istioctl install --set profile=default -y
1
2
3
4
5
6
7
8
9
10
11
12
istioctl profile list
Istio configuration profiles:
    **ambient**
    default
    demo
    empty
    minimal
    openshift
    openshift-ambient
    preview
    remote
    stable
  • 우리가 실습할 default 네임스페이스에 istio-proxy sidecar를 주입한다. (Label)
1
2
3
kubectl label namespace default istio-injection=enabled

namespace/default labeled
1
2
3
4
5
6
7
8
k get ns -L istio-injection
NAME                 STATUS   AGE     ISTIO-INJECTION
default              Active   5m29s   enabled
istio-system         Active   118s
kube-node-lease      Active   5m29s
kube-public          Active   5m29s
kube-system          Active   5m29s
local-path-storage   Active   5m25s

Ingress gateway 테스트

  • 이제 Ingress gateway 실습을 위해 호스트를 지정한다.
1
2
3
4
5
6
export MYDOMAIN="dongmin.test"

echo "127.0.0.1 $MYDOMAIN" | sudo tee -a /etc/hosts

Password:
127.0.0.1 dongmin.test
1
2
3
4
5
6
docker inspect -f '' myk8s-control-plane

172.18.0.2
echo -e "172.18.0.2 dongmin.test" | tee -a /etc/hosts

172.18.0.2 dongmin.test
  • 서비스 배포: Istio는 K8s에서 identity는 Service Account를 기반으로 하기에 서비스 어카운트도 같이 배포한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kans-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-websrv
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      serviceAccountName: kans-nginx
      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
  • Istio Gateway/VirtualService 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cat <<EOF | kubectl apply -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

ingressgateway를 살펴보면, 관련 설정을 Envoy에 완료한 것을 볼 수 있다.

image.png

이제 도메인으로 실제 접속해보면, 아래와 같이 정상 통신된다.

image.png

Proxy-config 확인

  • 이제 proxy-config를 확인해본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
docker exec -it myk8s-control-plane istioctl proxy-config all deploy-websrv-778ffd6947-mv2wj.default
Istio Version:       1.23.2
Istio Proxy Version: 6c72b2179f5a58988b920a55b0be8346de3f7b35
Envoy Version:       1.31.2-dev/Clean/RELEASE/BoringSSL

NAME                                                                            SERVICE FQDN                                            PORT      SUBSET     DIRECTION     TYPE             DESTINATION RULE
cluster/inbound|80||                                                                                                                    80        -          inbound       ORIGINAL_DST
cluster/BlackHoleCluster                                                        cluster/BlackHoleCluster                                -         -          -             STATIC
cluster/InboundPassthroughCluster                                               cluster/InboundPassthroughCluster                       -         -          -             ORIGINAL_DST
cluster/PassthroughCluster                                                      cluster/PassthroughCluster                              -         -          -             ORIGINAL_DST
cluster/agent                                                                   cluster/agent                                           -         -          -             STATIC
cluster/outbound|80||istio-ingressgateway.istio-system.svc.cluster.local        istio-ingressgateway.istio-system.svc.cluster.local     80        -          outbound      EDS
cluster/outbound|443||istio-ingressgateway.istio-system.svc.cluster.local       istio-ingressgateway.istio-system.svc.cluster.local     443       -          outbound      EDS
cluster/outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local     istio-ingressgateway.istio-system.svc.cluster.local     15021     -          outbound      EDS
cluster/outbound|443||istiod.istio-system.svc.cluster.local                     istiod.istio-system.svc.cluster.local                   443       -          outbound      EDS
cluster/outbound|15010||istiod.istio-system.svc.cluster.local                   istiod.istio-system.svc.cluster.local                   15010     -          outbound      EDS
cluster/outbound|15012||istiod.istio-system.svc.cluster.local                   istiod.istio-system.svc.cluster.local                   15012     -          outbound      EDS
cluster/outbound|15014||istiod.istio-system.svc.cluster.local                   istiod.istio-system.svc.cluster.local                   15014     -          outbound      EDS
cluster/outbound|53||kube-dns.kube-system.svc.cluster.local                     kube-dns.kube-system.svc.cluster.local                  53        -          outbound      EDS
cluster/outbound|9153||kube-dns.kube-system.svc.cluster.local                   kube-dns.kube-system.svc.cluster.local                  9153      -          outbound      EDS
cluster/outbound|443||kubernetes.default.svc.cluster.local                      kubernetes.default.svc.cluster.local                    443       -          outbound      EDS
cluster/prometheus_stats                                                        cluster/prometheus_stats                                -         -          -             STATIC
cluster/sds-grpc                                                                cluster/sds-grpc                                        -         -          -             STATIC
cluster/outbound|80||svc-clusterip.default.svc.cluster.local                    svc-clusterip.default.svc.cluster.local                 80        -          outbound      EDS
cluster/xds-grpc                                                                cluster/xds-grpc                                        -         -          -             STATIC

Listener는 각 서비스에 대한 요청을 받는 곳으로 아래와 같이 실제 서비스 IP에 대한 모든 Listener를 확인할 수 있다.

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
NAME                       ADDRESSES   PORT  MATCH                                                               DESTINATION
listener/10.200.1.10_53    10.200.1.10 53    ALL                                                                 Cluster: outbound|53||kube-dns.kube-system.svc.cluster.local
listener/0.0.0.0_80        0.0.0.0     80    Trans: raw_buffer; App: http/1.1,h2c                                Route: 80
listener/0.0.0.0_80        0.0.0.0     80    ALL                                                                 PassthroughCluster
listener/10.200.1.34_80    10.200.1.34 80    Trans: raw_buffer; App: http/1.1,h2c                                Route: svc-clusterip.default.svc.cluster.local:80
listener/10.200.1.34_80    10.200.1.34 80    ALL                                                                 Cluster: outbound|80||svc-clusterip.default.svc.cluster.local
listener/10.200.1.1_443    10.200.1.1  443   ALL                                                                 Cluster: outbound|443||kubernetes.default.svc.cluster.local
listener/10.200.1.59_443   10.200.1.59 443   ALL                                                                 Cluster: outbound|443||istiod.istio-system.svc.cluster.local
listener/10.200.1.73_443   10.200.1.73 443   ALL                                                                 Cluster: outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
listener/10.200.1.10_9153  10.200.1.10 9153  Trans: raw_buffer; App: http/1.1,h2c                                Route: kube-dns.kube-system.svc.cluster.local:9153
listener/10.200.1.10_9153  10.200.1.10 9153  ALL                                                                 Cluster: outbound|9153||kube-dns.kube-system.svc.cluster.local
listener/virtualOutbound   0.0.0.0     15001 ALL                                                                 PassthroughCluster
...
NAME                                                                VHOST NAME                                                    DOMAINS                                              MATCH                  VIRTUAL SERVICE
route/svc-clusterip.default.svc.cluster.local:80                    svc-clusterip.default.svc.cluster.local:80                    *                                                    /*
route/istio-ingressgateway.istio-system.svc.cluster.local:15021     istio-ingressgateway.istio-system.svc.cluster.local:15021     *                                                    /*
route/80                                                            istio-ingressgateway.istio-system.svc.cluster.local:80        istio-ingressgateway.istio-system, 10.200.1.73       /*
route/80                                                            svc-clusterip.default.svc.cluster.local:80                    svc-clusterip, svc-clusterip.default + 1 more...     /*
route/15010                                                         istiod.istio-system.svc.cluster.local:15010                   istiod.istio-system, 10.200.1.59                     /*
route/15014                                                         istiod.istio-system.svc.cluster.local:15014                   istiod.istio-system, 10.200.1.59                     /*
route/kube-dns.kube-system.svc.cluster.local:9153                   kube-dns.kube-system.svc.cluster.local:9153                   *                                                    /*
route/inbound|80||                                                  inbound|http|80                                               *                                                    /*
route/                                                              backend                                                       *                                                    /stats/prometheus*
route/                                                              backend                                                       *                                                    /healthz/ready*
route/InboundPassthroughCluster                                     inbound|http|0                                                *                                                    /*
route/InboundPassthroughCluster                                     inbound|http|0                                                *                                                    /*
route/inbound|80||                                                  inbound|http|80                                               *                                                    /*

엔드포인트 부분을 살펴보면, 실제 파드 엔드포인트(10.10.0.7)와 istio와 관련된 엔드포인트와 CoreDNS 관련 엔드포인트(dns port 53사용)를 확인할 수 있다.

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NAME                                                      STATUS      LOCALITY     CLUSTER
endpoint/10.10.0.7:80                                     HEALTHY                  inbound|80||
endpoint/127.0.0.1:15020                                  HEALTHY                  agent
endpoint/10.10.0.6:8080                                   HEALTHY                  outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
endpoint/10.10.0.6:8443                                   HEALTHY                  outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
endpoint/10.10.0.6:15021                                  HEALTHY                  outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
endpoint/10.10.0.5:15017                                  HEALTHY                  outbound|443||istiod.istio-system.svc.cluster.local
endpoint/10.10.0.5:15010                                  HEALTHY                  outbound|15010||istiod.istio-system.svc.cluster.local
endpoint/10.10.0.5:15012                                  HEALTHY                  outbound|15012||istiod.istio-system.svc.cluster.local
endpoint/10.10.0.5:15014                                  HEALTHY                  outbound|15014||istiod.istio-system.svc.cluster.local
endpoint/10.10.0.3:53                                     HEALTHY                  outbound|53||kube-dns.kube-system.svc.cluster.local
endpoint/10.10.0.4:53                                     HEALTHY                  outbound|53||kube-dns.kube-system.svc.cluster.local
endpoint/10.10.0.3:9153                                   HEALTHY                  outbound|9153||kube-dns.kube-system.svc.cluster.local
endpoint/10.10.0.4:9153                                   HEALTHY                  outbound|9153||kube-dns.kube-system.svc.cluster.local
...
  • 실제 kubectl 포트 포워딩을 통해 envoy 컨테이너 admin 페이지에 접속할 수 있다.
1
2
3
4
5
6
7
8
kubectl port-forward deployment/deploy-websrv 15000:15000 &

[1] 16270

Forwarding from 127.0.0.1:15000 -> 15000
Forwarding from [::1]:15000 -> 15000

open http://localhost:15000

image.png

Bookinfo(Sample Application) 및 istio Addon 설치

Istioctl에서 샘플로 제공해주는 bookinfo 애플리케이션을 설치해본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kubectl apply -f /istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml
service/details created
serviceaccount/bookinfo-details created
deployment.apps/details-v1 created
service/ratings created
serviceaccount/bookinfo-ratings created
deployment.apps/ratings-v1 created
service/reviews created
serviceaccount/bookinfo-reviews created
deployment.apps/reviews-v1 created
deployment.apps/reviews-v2 created
deployment.apps/reviews-v3 created
service/productpage created
serviceaccount/bookinfo-productpage created
deployment.apps/productpage-v1 created
1
2
3
4
kubectl apply -f /istio-$ISTIOV/samples/bookinfo/networking/bookinfo-gateway.yaml
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created
1
2
kubectl apply -f /istio-$ISTIOV/samples/addons 
kubectl rollout status deployment/kiali -n istio-system

실제 30000포트로 접속해보면, 아래와 같은 샘플 애플리케이션 페이지를 볼 수 있다.

image.png

kiali

kiali를 통해 트래픽 그래프를 확인할 수 있다.

현재는 1000개 정도의 요청을 보낸 상태인데, review를 잘 확인하면 적절하게 로드밸런싱이 되는 것을 확인할 수 있다.

image.png

This post is licensed under CC BY 4.0 by the author.