원래라면 AWS EKS에 istio를 설치하고 테스트를 바로 진행하려고 했는데, 로컬에서 테스트하며 정리를 해야할 것 같아서
kind로 생성한 로컬 클러스터에 istio를 설치하고 간단히 동작을 테스트 해보자
13디렉토리를 참고먼저 아래 yaml 파일을 작성한다. 로컬에서 접근할 것이기 때문에 control plane 노드에 30800 포트를 명시해준다. 이 포트를 이용해 server로 접근할 수 있다.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
image: kindest/node:v1.33.4
extraPortMappings:
# containerPort는 Kind 노드(컨테이너)의 포트
# 이 포트는 Service의 nodePort와 일치
- containerPort: 30800
# hostPort는 로컬 머신(PC)의 포트
hostPort: 30800
protocol: TCP
- role: worker
image: kindest/node:v1.33.4
labels:
role: server
- role: worker
image: kindest/node:v1.33.4
labels:
role: istiod
- role: worker
image: kindest/node:v1.33.4
labels:
role: istio-gateway
- role: worker
image: kindest/node:v1.33.4
labels:
role: istio-gateway
클러스터를 생성한다
kind create cluster --name 1-33 --config cluster.yaml
13/helm 디렉토리에서 helmfile을 이용해 설치한다.
먼저 base를 설치한다.
helmfile sync --selector name=istio-base
다음으로 istiod를 설치한다. nodeSelector에 role=istiod를 명시했다.
helmfile sync --selector name=istio-istiod
다음으로 ingress gateway를 설치한다. 서비스 타입은 NodePort이고 nodeSelector와 affinity를 이용해, 서로 다른 노드에 스케줄링 되도록 했다. (각 노드에 1개씩)
helmfile sync --selector name=istio-gateway
server namespace에 배포한다.
kubectl create ns server
kubectl -n server apply -f server.yaml
먼저 server, ingress gateway 이름을 변수로 저장한다.
export SERVER=$(kubectl get pod -n server | grep server | awk '{print $1}')
read GATEWAY1 GATEWAY2 <<< $(kubectl get pods -n istio-system -l istio=gateway -o jsonpath='{.items[*].metadata.name}')
export GATEWAY1 GATEWAY2
sniff로 8000포트 트래픽 기록을 시작한다. 각 파드마다 별도의 터미널에서 실행
kubectl sniff $SERVER -n server -f 'port 8000' --image ghcr.io/nefelim4ag/ksniff-helper:v4 --tcpdump-image ghcr.io/nefelim4ag/tcpdump:latest
kubectl sniff $GATEWAY1 -n istio-system -p -f 'port 8000' --image ghcr.io/nefelim4ag/ksniff-helper:v4 --tcpdump-image ghcr.io/nefelim4ag/tcpdump:latest
kubectl sniff $GATEWAY2 -n istio-system -p -f 'port 8000' --image ghcr.io/nefelim4ag/ksniff-helper:v4 --tcpdump-image ghcr.io/nefelim4ag/tcpdump:latest
로컬에서 python을 이용해 트래픽을 보낸다.
PORT=30800 ENDPOINT="/index" python ./client.py 10
istio ingress gateway 2개 중 1개에만 8000번 포트 트래픽을 확인할 수 있다.

아래는 server 파드의 트래픽이다

istio-system 네임스페이스의 파드 ip 확인 - 10.244.1.5
kubectl get pod -owide -n istio-system
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
istio-gateway-778cbb66c7-l65jd 1/1 Running 0 4h24m 10.244.4.7 1-33-worker3 <none> <none>
istio-gateway-778cbb66c7-vwr4m 1/1 Running 0 4h24m 10.244.1.5 1-33-worker4 <none> <none>
istiod-5764bfc-t25wc 1/1 Running 0 4h24m 10.244.3.3 1-33-worker2 <none> <none>
server 파드의 ip 확인 - 10.244.2.2
kubectl get pod -owide -n server
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ksniff-9kfzh 1/1 Running 0 40m 10.244.2.3 1-33-worker <none> <none>
server-66c6967c6-kjjz5 1/1 Running 0 4h20m 10.244.2.2 1-33-worker <none> <none>
docker로 구동중인 kind 클러스터의 노드 ip 확인 172.18.0.6
docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)
/1-33-worker - 172.18.0.5
/1-33-worker4 - 172.18.0.4
/1-33-worker2 - 172.18.0.3
/1-33-worker3 - 172.18.0.2
/1-33-control-plane - 172.18.0.6
client.py를 다시 실행하고 각 파드별로 소켓 연결 상태를 확인하면
// server
root@server-66c6967c6-kjjz5:/app# netstat -antp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 1/python3.13
tcp 0 0 10.244.2.2:8000 10.244.1.5:56740 ESTABLISHED 11/python3.13
// istio gateway
istio-proxy@istio-gateway-778cbb66c7-vwr4m:/$ netstat -antp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 17/envoy
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 17/envoy
tcp 0 0 0.0.0.0:15021 0.0.0.0:* LISTEN 17/envoy
tcp 0 0 0.0.0.0:15021 0.0.0.0:* LISTEN 17/envoy
tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 17/envoy
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 17/envoy
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 17/envoy
tcp 0 0 10.244.1.5:15021 10.244.1.1:54452 TIME_WAIT -
tcp 0 0 10.244.1.5:15021 10.244.1.1:46966 TIME_WAIT -
tcp 0 0 10.244.1.5:52162 10.96.225.207:15012 ESTABLISHED 1/pilot-agent
tcp 0 0 10.244.1.5:15021 10.244.1.1:50410 TIME_WAIT -
tcp 0 0 10.244.1.5:15021 10.244.1.1:59786 TIME_WAIT -
tcp 0 0 127.0.0.1:48208 127.0.0.1:15020 ESTABLISHED 17/envoy
tcp 0 0 10.244.1.5:15021 10.244.1.1:33434 TIME_WAIT -
tcp 0 0 10.244.1.5:80 172.18.0.6:3609 ESTABLISHED 17/envoy
tcp 0 0 127.0.0.1:53688 127.0.0.1:15020 ESTABLISHED 17/envoy
tcp 0 0 10.244.1.5:56740 10.244.2.2:8000 ESTABLISHED 17/envoy
tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent
tcp6 0 0 127.0.0.1:15020 127.0.0.1:48208 ESTABLISHED 1/pilot-agent
tcp6 0 0 127.0.0.1:15020 127.0.0.1:53688 ESTABLISHED 1/pilot-agent
연결상태를 정리하면 아래와 같다.
localhost:30800 -> control-plane node(172.18.0.6:3609)
control-plane node(172.18.0.6:3609) -> istio gateway(10.244.1.5:80)
istio gateway(10.244.1.5:56740) -> server(10.244.2.2:8000)
이렇게 연결되어 있는 상태에서, istio gateway를 삭제해보자
kubectl delete pod istio-gateway-778cbb66c7-vwr4m
client 로그는 아래와 같이 삭제 직후 요청이 실패하는 걸 확인할 수 있다.
--- Waiting for 10 seconds... ---
--- Sending keep-alive request #149 (GET /index) ---
--- Received response #149 ---
HTTP/1.1 200 OK
--- Waiting for 10 seconds... ---
--- Sending keep-alive request #150 (GET /index) ---
--- Received response #150 ---
HTTP/1.1 200 OK
--- Waiting for 10 seconds... ---
--- Sending keep-alive request #151 (GET /index) ---
*** TEST FAILED: Server closed connection (recv() returned 0 bytes) ***
*** TEST FAILED: Connection was reset by peer! ***
Error (Code: 54): Connection closed by peer
Socket closed.
패킷을 확인해보면 아래와 같다


client에서는 현재 graceful하게 종료를 하지 않아서 에러가 발생하지만 istio ingress gateway와 server에서는 FIN 패킷으로 정상 종료된 것을 확인할 수 있다.
로컬에서 테스트했을 때, client와 연결된 istio gateway 파드가 client <-> server 사이의 요청을 중개해주는 걸 확인할 수 있다.
istio gateway가 여러 개가 존재해도, 이미 연결되어 있는 client의 경우 연결 에러가 발생할 수 있다. 그래서 keep-alive 연결을 사용하는 client의 경우 retry 로직이 필요하다.
istio에서는 preStop, terminationDrainDuration를 활용할 수 있지만 이 또한 이미 연결된 client에서 retry를 해주는 게 바람직할 것 같다.