..

kans3) istio egressgateway 체험기

service mesh

마이크로서비스가 나오면서 내부 간의 트래픽 관리나 보안성을 강화하기 위해 서비스메시가 등장하게되었다. 🔗 그 중 하나인 istio에 대해 공부했다. 어플리케이션 간의 트래픽을 제어하려고 각 파드에 사이드카로 proxy가 함께 떠있다. 파드에 들어오는 트래픽을 이 proxy가 가로채고 관리하는 것이다. 이걸 하기 위해서 파드 안에 iptables를 쓰게되는데.. 한번 들어오는 패킷은 정신없게 Iptables를 타고 나가게되는거다..

1-0 이미지 출처 🔗

envoy

istio에서 프록시로 사용되는 Envoy proxy는 애플리케이션 간 네트워크가 투명하게 보여야 하고, 문제가 생기면 그 원인을 쉽게 파악할 수 있어야 한다는 철학에서 시작되었다고 문서에 나와있다. 🔗

구성요소

2-0

  • upstream: envoy가 요청을 전달하는 서버 또는 서비스
  • downstream: envoy에 연결하는 클라이언트 또는 애플리케이션
  • listener: 특정 IP와 포트에서 요청을 받아들이는 역할
  • filter: listener와 cluster 연결, 요청을 처리하는 파이프라인 모듈
  • cluster: endpoint들의 집합으로, envoy가 요청 할 수 있는 논리적 서비스
  • endpoint: 실제 접근 가능한 endpoint ip. 네트워크 노드

그림에서 보면 왼쪽에서 listener로 들어와서 정의된 네트워크 필터에 의해 클러스터로 전달되고, 클러스터는 로드밸런싱으로 알맞은 엔드포인드로 트래픽을 전달하게 된다.

istio

envoy proxy에서 모든 네트워크 트래픽을 가로채서 Istio에서 설정한 구성에 따라 패킷이 전달 될 수 있도록 한다. 구성을 크게 살펴보면, controlplane에서 프록시를 구성하고, dataplane에는 실제 동작 하는 부분으로 사이드카로 배포되어있는 envoy proxy가 있다.

구성요소

3-1

controlplane에 있는 istod 데몬 파드 하나에 여러 프로세스가 떠있다.

  • galley: 구성 관리, 수집, 배포를 담당. 클러스터 내의 정보를 수집한 것을 pilot에 전달한다.
  • pilot: 각 서비스에 대한 라우팅 규칙을 설정하고, 이를 proxy에게 배포한다.
  • citadel: 보안 관련 요소로, 연결 시 mTLS를 사용하여 암호화, 인증서 관리 등을 한다.

실습환경

# 스터디에서 제공해주는 스택파일로 올렸다.
❯ curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-7w.yaml
❯ aws cloudformation deploy --template-file kans-7w.yaml --stack-name mylab --parameter-overrides KeyName=nyoung SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

❯ 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
testpc     13.125.214.128  running
k3s-s   54.180.162.246  running
k3s-w2  3.38.104.74     running
k3s-w1  3.35.53.185     running

❯ ssh -i ./nyoung.pem ubuntu@54.180.162.246 

(⎈|default:N/A) root@k3s-s:~# k get no
NAME     STATUS   ROLES                  AGE     VERSION
k3s-s    Ready    control-plane,master   3h10m   v1.30.5+k3s1
k3s-w1   Ready    <none>                 3h10m   v1.30.5+k3s1
k3s-w2   Ready    <none>                 3h10m   v1.30.5+k3s1

istio 설치

istio 설치할 때, profile을 선택해서 설치 할 수가 있다. 어떤 구성요소르 설치할건지에따라 선택할 수 있다. 아래 실습에서는 demo profile을 선택해서 설치했다. 🔗

# istioctl 설치
(⎈|default:N/A) root@k3s-s:~# export ISTIOV=1.23.2
(⎈|default:N/A) root@k3s-s:~# echo "export ISTIOV=1.23.2" >> /etc/profile
(⎈|default:N/A) root@k3s-s:~# curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV TARGET_ARCH=x86_64 sh -
(⎈|default:N/A) root@k3s-s:~# tree istio-$ISTIOV -L 2 
(⎈|default:N/A) root@k3s-s:~# cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
(⎈|default:N/A) root@k3s-s:~# istioctl version --remote=false

(⎈|default:N/A) root@k3s-s:~# istioctl profile dump --config-path components.ingressGateways
- enabled: true
  name: istio-ingressgateway
(⎈|default:N/A) root@k3s-s:~# istioctl profile dump --config-path values.gateways.istio-ingressgateway
{}

(⎈|default:N/A) root@k3s-s:~# istioctl profile dump demo
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  components:
    base:
      enabled: true
    # egressgateway와 ingressgateway 활성화
    egressGateways:
    - enabled: true
      name: istio-egressgateway
    ingressGateways:
    - enabled: true
      name: istio-ingressgateway
    pilot:
      enabled: true
  hub: docker.io/istio
  profile: demo
  tag: 1.23.2
  values:
    defaultRevision: ""
    gateways:
      istio-egressgateway: {}
      istio-ingressgateway: {}
    global:
      configValidation: true
      istioNamespace: istio-system
    profile: demo
       
(⎈|default:N/A) root@k3s-s:~# istioctl profile dump demo > demo-profile.yaml
(⎈|default:N/A) root@k3s-s:~# istioctl install -f demo-profile.yaml -y
        |\
        | \
        |  \
        |   \
      /||    \
     / ||     \
    /  ||      \
   /   ||       \
  /    ||        \
 /     ||         \
/______||__________\
____________________
  \__       _____/
     \_____/

✔ Istio core installed ⛵️
✔ Istiod installed 🧠
...

# 기본으로 생성되어있는 pod,svc
(⎈|default:N/A) root@k3s-s:~# k get all -n istio-system
NAME                                        READY   STATUS    RESTARTS   AGE
pod/istio-egressgateway-57b6df4bcd-lpsdv    1/1     Running   0          72s
pod/istio-ingressgateway-5f9f654d46-pswq8   1/1     Running   0          72s
pod/istiod-7f8b586864-zrhkw                 1/1     Running   0          80s

NAME                           TYPE           CLUSTER-IP      EXTERNAL-IP                                   PORT(S)                                                                      AGE
service/istio-egressgateway    ClusterIP      10.10.200.109   <none>                                        80/TCP,443/TCP                                                               72s
service/istio-ingressgateway   LoadBalancer   10.10.200.31    192.168.10.10,192.168.10.101,192.168.10.102   15021:31535/TCP,80:30465/TCP,443:31462/TCP,31400:31633/TCP,15443:30813/TCP   72s
service/istiod                 ClusterIP      10.10.200.121   <none>                                        15010/TCP,15012/TCP,443/TCP,15014/TCP                                        80s

NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/istio-egressgateway    1/1     1            1           72s
deployment.apps/istio-ingressgateway   1/1     1            1           72s
deployment.apps/istiod                 1/1     1            1           80s

NAME                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/istio-egressgateway-57b6df4bcd    1         1         1       72s
replicaset.apps/istio-ingressgateway-5f9f654d46   1         1         1       72s
replicaset.apps/istiod-7f8b586864                 1         1         1       80s


# 눈으로 보기 좋게 kiali를 설치
(⎈|default:N/A) root@k3s-s:~# kubectl apply -f ~/istio-1.23.2/samples/addons 
(⎈|default:N/A) root@k3s-s:~# kubectl rollout status deployment/kiali -n istio-system
(⎈|default:N/A) root@k3s-s:~# kubectl patch svc -n istio-system kiali -p '{"spec":{"type":"NodePort"}}'

(⎈|default:N/A) root@k3s-s:~# KIALINodePort=$(kubectl get svc -n istio-system kiali -o jsonpath={.spec.ports[0].nodePort})
(⎈|default:N/A) root@k3s-s:~# echo -e "KIALI UI URL = http://$(curl -s ipinfo.io/ip):$KIALINodePort"
KIALI UI URL = http://54.180.162.246:31034

웹콘솔에 들어가서 트래픽 그래프에 가면, 아무런 통신이 없었기 때문에 빈페이지를 확일 할 수 있음

# envoy container가 생성될 수 있게 namespace에 라벨을 달아줬다.
(⎈|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   3h51m   enabled
istio-system      Active   8m38s
kube-node-lease   Active   3h51m
kube-public       Active   3h51m
kube-system       Active   3h51m


# netshoot이미지로 만든 테스트 파드를 보면, 2/2로 컨테이너가 두개인 것으로 확인
(⎈|default:N/A) root@k3s-s:~# k describe pod pod1
...
# init container가 뜨면서 iptables를 설정
Init Containers:
  istio-init:
    ...
    Image:         docker.io/istio/proxyv2:1.23.2
    Args:
      istio-iptables
      -p
      15001
      -z
      15006
      -u
      1337
      -m
      REDIRECT
      -i
      *
      -x

      -b
      *
      -d
      15090,15021,15020
      --log_output_level=default:info
Containers:
# 생성 시, 지정했던 이미지
  pod1:
    Image:         nicolaka/netshoot
    ... 
# envoy proxy가 올라오는 사이드카 컨테이너
  istio-proxy:
    Image:         docker.io/istio/proxyv2:1.23.2
    Args:
      proxy
      sidecar
      --domain
      $(POD_NAMESPACE).svc.cluster.local
      --proxyLogLevel=warning
      --proxyComponentLogLevel=misc:error
      --log_output_level=default:info
# 파드 이벤트 보면, init container > nginx container > istio-proxy container 순으로 올라왔다.    
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  60s   default-scheduler  Successfully assigned default/deploy-websrv-7cdd458999-pxgll to k3s-w1
  Normal  Pulled     60s   kubelet            Container image "docker.io/istio/proxyv2:1.23.2" already present on machine
  Normal  Created    60s   kubelet            Created container istio-init
  Normal  Started    60s   kubelet            Started container istio-init
  Normal  Pulling    59s   kubelet            Pulling image "nicolaka/netshoot"
  Normal  Pulled     55s   kubelet            Successfully pulled image "nicolaka/netshoot" in 4.921s (4.921s including waiting). Image size: 20506631 bytes.
  Normal  Created    55s   kubelet            Created container pod1
  Normal  Started    54s   kubelet            Started container pod1
  Normal  Pulled     54s   kubelet            Container image "docker.io/istio/proxyv2:1.23.2" already present on machine
  Normal  Created    54s   kubelet            Created container istio-proxy
  Normal  Started    54s   kubelet            Started container istio-proxy   

# node가 public ip를 가지고 있기 떄문에, 외부 호출은 잘된다.  
(⎈|default:N/A) root@k3s-s:~# k exec pod1 -it -- curl ifconfig.me
54.180.162.246   

egress gateway

istio api로는 한번 실패했다가.. gateway api로 다시 설치하고 성공했다. 이게 정신이 온전할 때 해봐야하는데, 자꾸 새벽에 숙제를 해서 그런지..😇🥲 참고한 문서는 이거… 🔗

# mesh 밖에 있는 엔드포인트에 대한 정보를 담고 있는 service entry 생성
(⎈|default:N/A) root@k3s-s:~# cat serviceentry.yaml
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: testserviceentry
  namespace: istio-system
spec:
  hosts:
  - ifconfig.me
  ports:
  - number: 80
    name: http-port
    protocol: HTTP
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
  
(⎈|default:N/A) root@k3s-s:~# k apply -f serviceentry.yaml
serviceentry.networking.istio.io/testserviceentry created

# kiali 화면에서 보면, pod1 > passthroughCluster로 나온다.
(⎈|default:N/A) root@k3s-s:~# k exec pod1 -- curl -sSL -o /dev/null -D - ifconfig.me
HTTP/1.1 200 OK
date: Sun, 20 Oct 2024 07:38:21 GMT
content-type: text/plain
content-length: 12
access-control-allow-origin: *
via: 1.1 google
x-envoy-upstream-service-time: 184
server: envoy

# gatewayAPI를 사용해서 crd 설치
(⎈|default:N/A) root@k3s-s:~# kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created

# gatewayclass 중에 istio 선택해서 gateway를 배포했다.
(⎈|default:N/A) root@k3s-s:~# k get gatewayclass
NAME           CONTROLLER                    ACCEPTED   AGE
istio          istio.io/gateway-controller   True       63m
istio-remote   istio.io/unmanaged-gateway    True       63m

# gatewayAPI 배포
(⎈|default:N/A) root@k3s-s:~# cat gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: egress-gateway
  namespace: istio-system
  annotations:
    networking.istio.io/service-type: ClusterIP
spec:
  gatewayClassName: istio
  listeners:
  - name: http
    hostname: ifconfig.me
    port: 80
    protocol: HTTP
    allowedRoutes:
# 다른 namespace에서 올 수 있게 설정
      namespaces:
        from: All
        
(⎈|default:N/A) root@k3s-s:~# k apply -f gateway.yaml
gateway.gateway.networking.k8s.io/egress-gateway created

# httpRoute 설정
(⎈|default:N/A) root@k3s-s:~# cat route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: direct-to-egress-gateway
  namespace: istio-system
spec:
  parentRefs:
  - kind: ServiceEntry
    group: networking.istio.io
    name: testserviceentry
    namespace: istio-system
  rules:
  - backendRefs:
    - name: egress-gateway-istio
      port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: forward-from-egress-gateway
  namespace: istio-system
spec:
  parentRefs:
  - name: egress-gateway
    namespace: istio-system
  hostnames:
  - ifconfig.me
  rules:
  - backendRefs:
    - kind: Hostname
      group: networking.istio.io
      name: ifconfig.me
      port: 80

(⎈|default:N/A) root@k3s-s:~# k apply -f route.yaml
httproute.gateway.networking.k8s.io/direct-to-egress-gateway created
httproute.gateway.networking.k8s.io/forward-from-egress-gateway created


(⎈|default:N/A) root@k3s-s:~# k exec pod1 -- curl -sSL -o /dev/null -D - ifconfig.me
HTTP/1.1 200 OK
date: Sun, 20 Oct 2024 08:19:49 GMT
content-type: text/plain
content-length: 12
access-control-allow-origin: *
via: 1.1 google
x-envoy-upstream-service-time: 158
server: envoy

4-3