Istio Ambient Mesh는 사이드카 프록시 없이 인프라에 통합된 데이터플레인 방식을 제공한다. 기존 Istio는 각 파드마다 사이드카 프록시를 배치하여 트래픽을 제어했다.


Ambient Mesh에서는 노드 단위로 공유되는 ztunnel 파드를 통해 파드 간 안전한 통신을 보장한다. ztunnel은 L4(전송 계층) 보안, 인증, 원격 측정, L4 정책을 제공한다. L7(애플리케이션 계층) 트래픽 제어나 정책이 필요할 때는 Waypoint Proxy 파드를 통해 처리한다.

 

Waypoint Proxy는 특정 네임스페이스 또는 서비스 계정 단위로 배포할 수 있다.

HBONE(HTTP Based Overlay Network Encapsulation) 프로토콜을 활용해 HTTP CONNECT 방식으로 트래픽을 터널링한다. HTTP/2와 mTLS를 사용하여 암호화 및 상호 인증을 제공한다.

ztunnel은 Istiod에서 xDS Config를 받아 네트워크 구성을 동적으로 적용한다.

ztunnel과 Waypoint Proxy는 서로 역할이 분리되어 보안성과 확장성을 높인다.

Ambient Mesh는 파드 스펙 변경이나 재시작 없이 메시 기능을 적용할 수 있다. 리소스 오버헤드가 기존 사이드카 방식보다 적고, 클러스터 자원 활용 효율이 높아진다. 또한 보안 측면에서 ztunnel은 노드 단위 키만 접근하므로 공격 범위가 제한된다.

기존 사이드카 방식과 Ambient Mesh 방식을 혼용해 사용할 수 있다. Ambient Mesh는 운영 단순화, 비용 절감, 확장성 개선 등 다양한 이점을 제공한다.

 

Istio의 사이드카 방식은 애플리케이션 파드의 사양을 수정하고 트래픽을 리디렉션해야 하므로, 사이드카 설치나 업그레이드 시 파드를 재시작해야 하는 부담이 있다. 각 파드별로 프록시 리소스를 최악의 상황에 맞춰 할당해야 하므로 클러스터 전체의 리소스 활용도가 저하된다. 사이드카가 트래픽 캡처와 HTTP 처리를 담당하면서 일부 비표준 HTTP 구현 애플리케이션에서는 트래픽이 차단되는 문제가 발생한다. 이러한 제약으로 인해 덜 침입적이고 사용하기 쉬운 서비스 메시 옵션의 필요성이 대두된다.

 

 

실습 환경 구성

```bash
# 
kind create cluster --name **myk8s** --image kindest/node:**v1.32.2** --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: **control-plane**
  extraPortMappings:
  - containerPort: 30000 # Sample Application
    hostPort: 30000
  - containerPort: 30001 # Prometheus
    hostPort: 30001
  - containerPort: 30002 # Grafana
    hostPort: 30002
  - containerPort: 30003 # Kiali
    hostPort: 30003
  - containerPort: 30004 # Tracing
    hostPort: 30004
  - containerPort: 30005 # kube-ops-view
    hostPort: 30005
- role: **worker**
- role: **worker**
networking:
  podSubnet: 10.10.0.0/16
  serviceSubnet: 10.200.1.0/24
EOF

# 설치 확인
docker ps

# 노드에 기본 툴 설치
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'apt update && apt install tree psmisc lsof ipset wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'; echo; done
~~for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'DEBIAN_FRONTEND=noninteractive **apt install termshark -y**'; echo; done~~

# (옵션) kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=**NodePort**,service.main.ports.http.nodePort=**30005** --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

## kube-ops-view 접속 URL 확인
open "http://127.0.0.1:**30005**/#scale=1.5"
open "http://127.0.0.1:30005/#scale=1.3"
```
# kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역
docker network ls
docker inspect kind


# '테스트용 PC(mypc)' 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 지정 혹은 지정 없이 배포
docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # IP 지정 실행 시
혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # IP 지정 없이 실행 시
docker ps


# '웹서버(myweb1, myweb2)' 컨테이너 기동 : kind 도커 브리지를 사용
# https://hub.docker.com/r/hashicorp/http-echo
docker run -d --rm --name myweb1 --network kind --ip 172.18.0.101 hashicorp/http-echo -listen=:80 -text="myweb1 server"
docker run -d --rm --name myweb2 --network kind --ip 172.18.0.102 hashicorp/http-echo -listen=:80 -text="myweb2 server"
혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행
docker run -d --rm --name myweb1 --network kind hashicorp/http-echo -listen=:80 -text="myweb1 server"
docker run -d --rm --name myweb2 --network kind hashicorp/http-echo -listen=:80 -text="myweb2 server"
docker ps


# kind network 중 컨테이너(노드) IP(대역) 확인
docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
/myweb2 172.18.0.102
/myweb1 172.18.0.101
/mypc 172.18.0.100
/myk8s-control-plane 172.18.0.2

# 동일한 docker network(kind) 내부에서 컨테이너 이름 기반 도메인 통신 가능 확인!
docker exec -it mypc curl myweb1
docker exec -it mypc curl myweb2

docker exec -it mypc curl 172.18.0.101
docker exec -it mypc curl 172.18.0.102
# MetalLB 배포
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml


# 확인
kubectl get crd
kubectl get pod -n metallb-system


# IPAddressPool, L2Advertisement 설정
cat << EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.201-172.18.255.220
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default
EOF

# 확인
kubectl get IPAddressPool,L2Advertisement -A
# myk8s-control-plane 진입 후 설치 진행
docker exec -it myk8s-control-plane bash
-----------------------------------
# istioctl 설치
export ISTIOV=1.26.0
echo 'export ISTIOV=1.26.0' >> /root/.bashrc

curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
istioctl version --remote=false
client version: 1.26.0

# ambient 프로파일 컨트롤 플레인 배포
istioctl install --set profile=ambient --set meshConfig.accessLogFile=/dev/stdout --skip-confirmation
istioctl install --set profile=ambient --set meshConfig.enableTracing=true -y

# Install the Kubernetes Gateway API CRDs
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
  kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml

# 보조 도구 설치
kubectl apply -f istio-$ISTIOV/samples/addons
kubectl apply -f istio-$ISTIOV/samples/addons # nodePort 충돌 시 한번 더 입력

# 빠져나오기
exit
-----------------------------------

# 설치 확인 : istiod, istio-ingressgateway, crd 등
kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
kubectl get crd | grep istio.io
kubectl get crd | grep -v istio | grep -v metallb
kubectl get crd  | grep gateways
gateways.gateway.networking.k8s.io          2025-06-01T04:54:23Z
gateways.networking.istio.io                2025-06-01T04:53:51Z

kubectl api-resources | grep Gateway
gatewayclasses                      gc           gateway.networking.k8s.io/v1        false        GatewayClass
gateways                            gtw          gateway.networking.k8s.io/v1        true         Gateway
gateways                            gw           networking.istio.io/v1              true         Gateway

kubectl describe cm -n istio-system istio
...
Data
====
mesh:
----
accessLogFile: /dev/stdout
defaultConfig:
  discoveryAddress: istiod.istio-system.svc:15012
defaultProviders:
  metrics:
  - prometheus
enablePrometheusMerge: true
...

docker exec -it myk8s-control-plane istioctl proxy-status
NAME                           CLUSTER        CDS         LDS         EDS         RDS         ECDS        ISTIOD                     VERSION
ztunnel-25hpt.istio-system     Kubernetes     IGNORED     IGNORED     IGNORED     IGNORED     IGNORED     istiod-86b6b7ff7-x4787     1.26.0
ztunnel-4r4d4.istio-system     Kubernetes     IGNORED     IGNORED     IGNORED     IGNORED     IGNORED     istiod-86b6b7ff7-x4787     1.26.0
ztunnel-9rzzt.istio-system     Kubernetes     IGNORED     IGNORED     IGNORED     IGNORED     IGNORED     istiod-86b6b7ff7-x4787     1.26.0

docker exec -it myk8s-control-plane istioctl ztunnel-config workload
docker exec -it myk8s-control-plane istioctl ztunnel-config service

# iptables 규칙 확인
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'iptables-save'; echo; done


# NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'

# Prometheus 접속 : envoy, istio 메트릭 확인
open http://127.0.0.1:30001

# Grafana 접속
open http://127.0.0.1:30002

# Kiali 접속 : NodePort
open http://127.0.0.1:30003

# tracing 접속 : 예거 트레이싱 대시보드
open http://127.0.0.1:30004
# ztunnel 파드 확인 : 파드 이름 변수 지정
kubectl get pod -n istio-system -l app=ztunnel -owide
kubectl get pod -n istio-system -l app=ztunnel
ZPOD1NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[0].metadata.name}")
ZPOD2NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[1].metadata.name}")
ZPOD3NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[2].metadata.name}")
echo $ZPOD1NAME $ZPOD2NAME $ZPOD3NAME

#
kubectl describe pod -n istio-system -l app=ztunnel
...
Containers:
  istio-proxy:
    Container ID:  containerd://d81ca867bfd0c505f062ea181a070a8ab313df3591e599a22706a7f4f537ffc5
    Image:         docker.io/istio/ztunnel:1.26.0-distroless
    Image ID:      docker.io/istio/ztunnel@sha256:d711b5891822f4061c0849b886b4786f96b1728055333cbe42a99d0aeff36dbe
    Port:          15020/TCP
    ...
    Requests:
      cpu:      200m
      memory:   512Mi
    Readiness:  http-get http://:15021/healthz/ready delay=0s timeout=1s period=10s #success=1 #failure=3
    Environment:
      CA_ADDRESS:                        istiod.istio-system.svc:15012
      XDS_ADDRESS:                       istiod.istio-system.svc:15012
      RUST_LOG:                          info
      RUST_BACKTRACE:                    1
      ISTIO_META_CLUSTER_ID:             Kubernetes
      INPOD_ENABLED:                     true
      TERMINATION_GRACE_PERIOD_SECONDS:  30
      POD_NAME:                          ztunnel-9rzzt (v1:metadata.name)
      POD_NAMESPACE:                     istio-system (v1:metadata.namespace)
      NODE_NAME:                          (v1:spec.nodeName)
      INSTANCE_IP:                        (v1:status.podIP)
      SERVICE_ACCOUNT:                    (v1:spec.serviceAccountName)
      ISTIO_META_ENABLE_HBONE:           true
    Mounts:
      /tmp from tmp (rw)
      /var/run/secrets/istio from istiod-ca-cert (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-42r88 (ro)
      /var/run/secrets/tokens from istio-token (rw)
      /var/run/ztunnel from cni-ztunnel-sock-dir (rw)
    ...
Volumes:
  istio-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  43200
  istiod-ca-cert:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      istio-ca-root-cert
    Optional:  false
  cni-ztunnel-sock-dir:
    Type:          HostPath (bare host directory volume)
    Path:          /var/run/ztunnel
    HostPathType:  DirectoryOrCreate
...


#
kubectl krew install pexec
kubectl pexec $ZPOD1NAME -it -T -n istio-system -- bash
-------------------------------------------------------
whoami

ip -c addr
ifconfig

iptables -t mangle -S
iptables -t nat -S

ss -tnlp
ss -tnp

ss -xnp
Netid       State        Recv-Q        Send-Q                               Local Address:Port                Peer Address:Port        Process                                                                              
u_str       ESTAB        0             0                                                * 44981                          * 44982        users:(("ztunnel",pid=1,fd=13),("ztunnel",pid=1,fd=8),("ztunnel",pid=1,fd=6))       
u_seq       ESTAB        0             0                    /var/run/ztunnel/ztunnel.sock 47646                          * 46988                                                                                            
u_str       ESTAB        0             0                                                * 44982                          * 44981        users:(("ztunnel",pid=1,fd=7))                                                      
u_seq       ESTAB        0             0                                                * 46988                          * 47646        users:(("ztunnel",pid=1,fd=19)) 

ls -l  /var/run/ztunnel
total 0
srwxr-xr-x    1 root     root             0 Jun  1 04:54 ztunnel.sock

# 메트릭 정보 확인
curl -s http://localhost:15020/metrics

# Viewing Istiod state for ztunnel xDS resources
curl -s http://localhost:15000/config_dump

exit
-------------------------------------------------------

# 아래 ztunnel 파드도 확인해보자
kubectl pexec $ZPOD2NAME -it -T -n istio-system -- bash
kubectl pexec $ZPOD3NAME -it -T -n istio-system -- bash


# 노드에서 기본 정보 확인
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'ls -l /var/run/ztunnel'; echo; done


# ztunnel 데몬셋 파드 로그 확인
kubectl logs -n istio-system -l app=ztunnel -f
...
# ztunnel 파드 확인 : 파드 이름 변수 지정
kubectl get pod -n istio-system -l app=ztunnel -owide
kubectl get pod -n istio-system -l app=ztunnel
ZPOD1NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[0].metadata.name}")
ZPOD2NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[1].metadata.name}")
ZPOD3NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[2].metadata.name}")
echo $ZPOD1NAME $ZPOD2NAME $ZPOD3NAME

#
kubectl describe pod -n istio-system -l app=ztunnel
...
Containers:
  istio-proxy:
    Container ID:  containerd://d81ca867bfd0c505f062ea181a070a8ab313df3591e599a22706a7f4f537ffc5
    Image:         docker.io/istio/ztunnel:1.26.0-distroless
    Image ID:      docker.io/istio/ztunnel@sha256:d711b5891822f4061c0849b886b4786f96b1728055333cbe42a99d0aeff36dbe
    Port:          15020/TCP
    ...
    Requests:
      cpu:      200m
      memory:   512Mi
    Readiness:  http-get http://:15021/healthz/ready delay=0s timeout=1s period=10s #success=1 #failure=3
    Environment:
      CA_ADDRESS:                        istiod.istio-system.svc:15012
      XDS_ADDRESS:                       istiod.istio-system.svc:15012
      RUST_LOG:                          info
      RUST_BACKTRACE:                    1
      ISTIO_META_CLUSTER_ID:             Kubernetes
      INPOD_ENABLED:                     true
      TERMINATION_GRACE_PERIOD_SECONDS:  30
      POD_NAME:                          ztunnel-9rzzt (v1:metadata.name)
      POD_NAMESPACE:                     istio-system (v1:metadata.namespace)
      NODE_NAME:                          (v1:spec.nodeName)
      INSTANCE_IP:                        (v1:status.podIP)
      SERVICE_ACCOUNT:                    (v1:spec.serviceAccountName)
      ISTIO_META_ENABLE_HBONE:           true
    Mounts:
      /tmp from tmp (rw)
      /var/run/secrets/istio from istiod-ca-cert (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-42r88 (ro)
      /var/run/secrets/tokens from istio-token (rw)
      /var/run/ztunnel from cni-ztunnel-sock-dir (rw)
    ...
Volumes:
  istio-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  43200
  istiod-ca-cert:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      istio-ca-root-cert
    Optional:  false
  cni-ztunnel-sock-dir:
    Type:          HostPath (bare host directory volume)
    Path:          /var/run/ztunnel
    HostPathType:  DirectoryOrCreate
...


#
kubectl krew install pexec
kubectl pexec $ZPOD1NAME -it -T -n istio-system -- bash
-------------------------------------------------------
whoami

ip -c addr
ifconfig

iptables -t mangle -S
iptables -t nat -S

ss -tnlp
ss -tnp

ss -xnp
Netid       State        Recv-Q        Send-Q                               Local Address:Port                Peer Address:Port        Process                                                                              
u_str       ESTAB        0             0                                                * 44981                          * 44982        users:(("ztunnel",pid=1,fd=13),("ztunnel",pid=1,fd=8),("ztunnel",pid=1,fd=6))       
u_seq       ESTAB        0             0                    /var/run/ztunnel/ztunnel.sock 47646                          * 46988                                                                                            
u_str       ESTAB        0             0                                                * 44982                          * 44981        users:(("ztunnel",pid=1,fd=7))                                                      
u_seq       ESTAB        0             0                                                * 46988                          * 47646        users:(("ztunnel",pid=1,fd=19)) 

ls -l  /var/run/ztunnel
total 0
srwxr-xr-x    1 root     root             0 Jun  1 04:54 ztunnel.sock

# 메트릭 정보 확인
curl -s http://localhost:15020/metrics

# Viewing Istiod state for ztunnel xDS resources
curl -s http://localhost:15000/config_dump

exit
-------------------------------------------------------

# 아래 ztunnel 파드도 확인해보자
kubectl pexec $ZPOD2NAME -it -T -n istio-system -- bash
kubectl pexec $ZPOD3NAME -it -T -n istio-system -- bash


# 노드에서 기본 정보 확인
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'ls -l /var/run/ztunnel'; echo; done


# ztunnel 데몬셋 파드 로그 확인
kubectl logs -n istio-system -l app=ztunnel -f
...

 

 

#
docker exec -it myk8s-control-plane ls -l istio-1.26.0
total 40
-rw-r--r--  1 root root 11357 May  7 11:05 LICENSE
-rw-r--r--  1 root root  6927 May  7 11:05 README.md
drwxr-x---  2 root root  4096 May  7 11:05 bin
-rw-r-----  1 root root   983 May  7 11:05 manifest.yaml
drwxr-xr-x  4 root root  4096 May  7 11:05 manifests
drwxr-xr-x 27 root root  4096 May  7 11:05 samples
drwxr-xr-x  3 root root  4096 May  7 11:05 tools

# Deploy the Bookinfo sample application:
docker exec -it myk8s-control-plane kubectl apply -f istio-1.26.0/samples/bookinfo/platform/kube/bookinfo.yaml

# 확인
kubectl get deploy,pod,svc,ep
docker exec -it myk8s-control-plane istioctl ztunnel-config service
docker exec -it myk8s-control-plane istioctl ztunnel-config workload
docker exec -it myk8s-control-plane istioctl proxy-status


# 통신 확인 : ratings 에서 productpage 페이지
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>"


# 요청 테스트용 파드 생성 : netshoot
kubectl create sa netshoot

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: netshoot
spec:
  serviceAccountName: netshoot
  nodeName: myk8s-control-plane
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 요청 확인
kubectl exec -it netshoot -- curl -sS productpage:9080/productpage | grep -i title

# 반복 요청
while true; do kubectl exec -it netshoot -- curl -sS productpage:9080/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done

 

#
docker exec -it myk8s-control-plane cat istio-1.26.0/samples/bookinfo/gateway-api/bookinfo-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  gatewayClassName: istio
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: bookinfo
spec:
  parentRefs:
  - name: bookinfo-gateway
  rules:
  - matches:
    - path:
        type: Exact
        value: /productpage
    - path:
        type: PathPrefix
        value: /static
    - path:
        type: Exact
        value: /login
    - path:
        type: Exact
        value: /logout
    - path:
        type: PathPrefix
        value: /api/v1/products
    backendRefs:
    - name: productpage
      port: 9080

docker exec -it myk8s-control-plane kubectl apply -f istio-1.26.0/samples/bookinfo/gateway-api/bookinfo-gateway.yaml

# 확인
kubectl get gateway
NAME               CLASS   ADDRESS          PROGRAMMED   AGE
bookinfo-gateway   istio   172.18.255.201   True         75s

kubectl get HTTPRoute
NAME       HOSTNAMES   AGE
bookinfo               101s

kubectl get svc,ep bookinfo-gateway-istio
NAME                             TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                        AGE
service/bookinfo-gateway-istio   LoadBalancer   10.200.1.122   172.18.255.201   15021:30870/TCP,80:31570/TCP   2m37s

NAME                               ENDPOINTS                      AGE
endpoints/bookinfo-gateway-istio   10.10.1.6:15021,10.10.1.6:80   2m37s

kubectl get pod -l gateway.istio.io/managed=istio.io-gateway-controller -owide
NAME                                      READY   STATUS    RESTARTS   AGE     IP          NODE            NOMINATED NODE   READINESS GATES
bookinfo-gateway-istio-6cbd9bcd49-fwqqp   1/1     Running   0          3m45s   10.10.1.6   myk8s-worker2   <none>           <none>

# 접속 확인
docker ps

kubectl get svc bookinfo-gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
GWLB=$(kubectl get svc bookinfo-gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker exec -it mypc curl $GWLB/productpage -v
docker exec -it mypc curl $GWLB/productpage -I

# 반복 요청 : 아래 mypc 컨테이너에서 반복 요청 계속 해두기!
GWLB=$(kubectl get svc bookinfo-gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
while true; do docker exec -it mypc curl $GWLB/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done


# 자신의 로컬 PC에서 접속 시도
kubectl patch svc bookinfo-gateway-istio -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 80, "nodePort": 30000}]}}'
kubectl get svc bookinfo-gateway-istio

open "http://127.0.0.1:30000/productpage"

# 반복 요청
while true; do curl -s http://127.0.0.1:30000/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done

 

Adding your application to ambient- Docs

  • The namespace or pod has the label istio.io/dataplane-mode=ambient
  • The pod does not have the opt-out label istio.io/dataplane-mode=none
# 디폴트 네임스페이서 모든 파드들에 ambient mesh 통신 적용 설정
# You can enable all pods in a given namespace to be part of the ambient mesh by simply labeling the namespace:
kubectl label namespace default istio.io/dataplane-mode=ambient

# 파드 정보 확인 : 사이트카가 없다! , 파드 수명 주기에 영향도 없다! -> mTLS 암호 통신 제공, L4 텔레메트리(메트릭) 제공
docker exec -it myk8s-control-plane istioctl proxy-status
kubectl get pod

#
docker exec -it myk8s-control-plane istioctl ztunnel-config workload
NAMESPACE          POD NAME                                    ADDRESS    NODE                WAYPOINT PROTOCOL
default            details-v1-766844796b-nfsh8                 10.10.2.16 myk8s-worker        None     HBONE
default            netshoot                                    10.10.0.7  myk8s-control-plane None     HBONE
default            productpage-v1-54bb874995-xkq54             10.10.2.20 myk8s-worker        None     HBONE
...

docker exec -it myk8s-control-plane istioctl ztunnel-config workload --address 10.10.2.20
NAMESPACE POD NAME                        ADDRESS    NODE         WAYPOINT PROTOCOL
default   productpage-v1-54bb874995-xkq54 10.10.2.20 myk8s-worker None     HBONE

docker exec -it myk8s-control-plane istioctl ztunnel-config workload --address 10.10.2.20 -o json
[
    {
        "uid": "Kubernetes//Pod/default/productpage-v1-54bb874995-xkq54",
        "workloadIps": [
            "10.10.2.20"
        ],
        "protocol": "HBONE",
        "name": "productpage-v1-54bb874995-xkq54",
        "namespace": "default",
        "serviceAccount": "bookinfo-productpage",
        "workloadName": "productpage-v1",
        "workloadType": "pod",
        "canonicalName": "productpage",
        "canonicalRevision": "v1",
        "clusterId": "Kubernetes",
        "trustDomain": "cluster.local",
        "locality": {},
        "node": "myk8s-worker",
        "status": "Healthy",
...


#
PPOD=$(kubectl get pod -l app=productpage -o jsonpath='{.items[0].metadata.name}')

kubectl pexec $PPOD -it -T -- bash
-------------------------------------------------------
iptables-save
iptables -t mangle -S
iptables -t nat -S
ss -tnlp
ss -tnp
ss -xnp
ls -l  /var/run/ztunnel

# 메트릭 정보 확인
curl -s http://localhost:15020/metrics | grep '^[^#]'
...

# Viewing Istiod state for ztunnel xDS resources
curl -s http://localhost:15000/config_dump

exit
-------------------------------------------------------


# 노드에서 ipset 확인 : 파드들의 ip를 멤버로 관리 확인
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node ipset list; echo; done
...
Members:
10.10.2.15 comment "57009539-36bb-42e0-bdac-4c2356fabbd3"
10.10.2.19 comment "64f46320-b85d-4e10-ab97-718d4e282116"
10.10.2.17 comment "b5ff9b4c-722f-48ef-a789-a95485fe9fa8"
10.10.2.18 comment "c6b368f7-6b6f-4d1d-8dbe-4ee85d7c9c22"
10.10.2.16 comment "cd1b1016-5570-4492-ba3a-4299790029d9"
10.10.2.20 comment "2b97238b-3b37-4a13-87a2-70755cb225e6"

# istio-cni-node 로그 확인
kubectl -n istio-system logs -l k8s-app=istio-cni-node -f
...

# ztunnel 파드 로그 모니터링 : IN/OUT 트래픽 정보
kubectl -n istio-system logs -l app=ztunnel -f | egrep "inbound|outbound"
2025-06-01T06:37:49.162266Z     info    access  connection complete     src.addr=10.10.2.20:48392 src.workload="productpage-v1-54bb874995-xkq54" src.namespace="default" src.identity="spiffe://cluster.local/ns/default/sa/bookinfo-productpage" dst.addr=10.10.2.18:15008 dst.hbone_addr=10.10.2.18:9080 dst.service="reviews.default.svc.cluster.local" dst.workload="reviews-v2-556d6457d-4r8n8" dst.namespace="default" dst.identity="spiffe://cluster.local/ns/default/sa/bookinfo-reviews" direction="inbound" bytes_sent=602 bytes_recv=192 duration="31ms"
2025-06-01T06:37:49.162347Z     info    access  connection complete     src.addr=10.10.2.20:53412 src.workload="productpage-v1-54bb874995-xkq54" src.namespace="default" src.identity="spiffe://cluster.local/ns/default/sa/bookinfo-productpage" dst.addr=10.10.2.18:15008 dst.hbone_addr=10.10.2.18:9080 dst.service="reviews.default.svc.cluster.local" dst.workload="reviews-v2-556d6457d-4r8n8" dst.namespace="default" dst.identity="spiffe://cluster.local/ns/default/sa/bookinfo-reviews" direction="outbound" bytes_sent=192 bytes_recv=602 duration="31ms"


# ztunnel 파드 확인 : 파드 이름 변수 지정
kubectl get pod -n istio-system -l app=ztunnel -owide
kubectl get pod -n istio-system -l app=ztunnel
ZPOD1NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[0].metadata.name}")
ZPOD2NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[1].metadata.name}")
ZPOD3NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[2].metadata.name}")
echo $ZPOD1NAME $ZPOD2NAME $ZPOD3NAME

#
kubectl pexec $ZPOD1NAME -it -T -n istio-system -- bash
-------------------------------------------------------
iptables -t mangle -S
iptables -t nat -S
ss -tnlp
ss -tnp
ss -xnp
ls -l  /var/run/ztunnel

# 메트릭 정보 확인
curl -s http://localhost:15020/metrics | grep '^[^#]'
...

# Viewing Istiod state for ztunnel xDS resources
curl -s http://localhost:15000/config_dump

exit
-------------------------------------------------------

# netshoot 파드만 ambient mode 에서 제외해보자
docker exec -it myk8s-control-plane istioctl ztunnel-config workload
kubectl label pod netshoot istio.io/dataplane-mode=none
docker exec -it myk8s-control-plane istioctl ztunnel-config workload
NAMESPACE          POD NAME                                    ADDRESS    NODE                WAYPOINT PROTOCOL
default            netshoot                                    10.10.0.7  myk8s-control-plane None     TCP

default NS는 HBONE으로 동작하는것을 알 수 있다.

 

PS.

기존 사이드카 구성에서 ambient mode로 변경하려면... 어떡해야할까..

https://ambientmesh.io/docs/setup/sidecar-migration/

 

Migrating to ambient mesh from Istio in sidecar mode

The Solo ambient mesh migration tool provides a prescriptive path for migrating from Istio’s sidecar mode to ambient mode. This migration is zero-downtime when used with the free Solo builds of Istio, but can also translate Kubernetes manifests for users

ambientmesh.io

 

 

ztunnel-config

# A group of commands used to update or retrieve Ztunnel configuration from a Ztunnel instance.
docker exec -it myk8s-control-plane istioctl ztunnel-config
  all         Retrieves all configuration for the specified Ztunnel pod.
  certificate Retrieves certificate for the specified Ztunnel pod.
  connections Retrieves connections for the specified Ztunnel pod.
  log         Retrieves logging levels of the Ztunnel instance in the specified pod.
  policy      Retrieves policies for the specified Ztunnel pod.
  service     Retrieves services for the specified Ztunnel pod.
  workload    Retrieves workload configuration for the specified Ztunnel pod.
...

docker exec -it myk8s-control-plane istioctl ztunnel-config service
NAMESPACE      SERVICE NAME            SERVICE VIP  WAYPOINT ENDPOINTS
default        bookinfo-gateway-istio  10.200.1.122 None     1/1
default        details                 10.200.1.202 None     1/1
default        kubernetes              10.200.1.1   None     1/1
default        productpage             10.200.1.207 None     1/1
default        ratings                 10.200.1.129 None     1/1
default        reviews                 10.200.1.251 None     3/3
...

docker exec -it myk8s-control-plane istioctl ztunnel-config service --service-namespace default --node myk8s-worker
docker exec -it myk8s-control-plane istioctl ztunnel-config service --service-namespace default --node myk8s-worker2
docker exec -it myk8s-control-plane istioctl ztunnel-config service --service-namespace default --node myk8s-worker2 -o json
...
    {
        "name": "productpage",
        "namespace": "default",
        "hostname": "productpage.default.svc.cluster.local",
        "vips": [
            "/10.200.1.207"
        ],
        "ports": {
            "9080": 9080
        },
        "endpoints": {
            "Kubernetes//Pod/default/productpage-v1-54bb874995-xkq54": {
                "workloadUid": "Kubernetes//Pod/default/productpage-v1-54bb874995-xkq54",
                "service": "",
                "port": {
                    "9080": 9080
                }
            }
        },
        "ipFamilies": "IPv4"
    },
...

docker exec -it myk8s-control-plane istioctl ztunnel-config workload
docker exec -it myk8s-control-plane istioctl ztunnel-config workload --workload-namespace default
docker exec -it myk8s-control-plane istioctl ztunnel-config workload --workload-namespace default --node myk8s-worker2
docker exec -it myk8s-control-plane istioctl ztunnel-config workload --workload-namespace default --node myk8s-worker -o json
...
    {
        "uid": "Kubernetes//Pod/default/productpage-v1-54bb874995-xkq54",
        "workloadIps": [
            "10.10.2.20"
        ],
        "protocol": "HBONE",
        "name": "productpage-v1-54bb874995-xkq54",
        "namespace": "default",
        "serviceAccount": "bookinfo-productpage",
        "workloadName": "productpage-v1",
        "workloadType": "pod",
        "canonicalName": "productpage",
        "canonicalRevision": "v1",
        "clusterId": "Kubernetes",
        "trustDomain": "cluster.local",
        "locality": {},
        "node": "myk8s-worker",
        "status": "Healthy",
        "hostname": "",
        "capacity": 1,
        "applicationTunnel": {
            "protocol": ""
        }
    },
...

docker exec -it myk8s-control-plane istioctl ztunnel-config certificate --node myk8s-worker
CERTIFICATE NAME                                              TYPE     STATUS        VALID CERT     SERIAL NUMBER                        NOT AFTER                NOT BEFORE
spiffe://cluster.local/ns/default/sa/bookinfo-details         Leaf     Available     true           6399f0ac1d1f508088c731791930a03a     2025-06-02T06:21:46Z     2025-06-01T06:19:46Z
spiffe://cluster.local/ns/default/sa/bookinfo-details         Root     Available     true           8a432683a288fb4b61d5775dcf47019d     2035-05-30T04:53:58Z     2025-06-01T04:53:58Z
spiffe://cluster.local/ns/default/sa/bookinfo-productpage     Leaf     Available     true           f727d33914e23029b3c889c67efe12e1     2025-06-02T06:21:46Z     2025-06-01T06:19:46Z
spiffe://cluster.local/ns/default/sa/bookinfo-productpage     Root     Available     true           8a432683a288fb4b61d5775dcf47019d     2035-05-30T04:53:58Z     2025-06-01T04:53:58Z
spiffe://cluster.local/ns/default/sa/bookinfo-ratings         Leaf     Available     true           d1af8176f5047f620d7795a4775869ad     2025-06-02T06:21:46Z     2025-06-01T06:19:46Z
spiffe://cluster.local/ns/default/sa/bookinfo-ratings         Root     Available     true           8a432683a288fb4b61d5775dcf47019d     2035-05-30T04:53:58Z     2025-06-01T04:53:58Z
spiffe://cluster.local/ns/default/sa/bookinfo-reviews         Leaf     Available     true           af013ce3f7dca5dc1bead36455155d65     2025-06-02T06:21:46Z     2025-06-01T06:19:46Z
spiffe://cluster.local/ns/default/sa/bookinfo-reviews         Root     Available     true           8a432683a288fb4b61d5775dcf47019d     2035-05-30T04:53:58Z     2025-06-01T04:53:58Z

docker exec -it myk8s-control-plane istioctl ztunnel-config certificate --node myk8s-worker -o json
...


docker exec -it myk8s-control-plane istioctl ztunnel-config connections --node myk8s-worker
WORKLOAD                                DIRECTION LOCAL                                        REMOTE                                                REMOTE TARGET                          PROTOCOL
productpage-v1-54bb874995-xkq54.default Inbound   productpage-v1-54bb874995-xkq54.default:9080 bookinfo-gateway-istio-6cbd9bcd49-fwqqp.default:33052                                        HBONE
ratings-v1-5dc79b6bcd-64kcm.default     Inbound   ratings-v1-5dc79b6bcd-64kcm.default:9080     reviews-v2-556d6457d-4r8n8.default:56440              ratings.default.svc.cluster.local      HBONE
reviews-v2-556d6457d-4r8n8.default      Outbound  reviews-v2-556d6457d-4r8n8.default:41722     ratings-v1-5dc79b6bcd-64kcm.default:15008             ratings.default.svc.cluster.local:9080 HBONE

docker exec -it myk8s-control-plane istioctl ztunnel-config connections --node myk8s-worker --raw 
WORKLOAD                                DIRECTION LOCAL            REMOTE           REMOTE TARGET                     PROTOCOL
productpage-v1-54bb874995-xkq54.default Inbound   10.10.2.20:9080  10.10.1.6:33064                                    HBONE
productpage-v1-54bb874995-xkq54.default Inbound   10.10.2.20:9080  10.10.1.6:33052                                    HBONE
ratings-v1-5dc79b6bcd-64kcm.default     Inbound   10.10.2.15:9080  10.10.2.18:56440 ratings.default.svc.cluster.local HBONE
ratings-v1-5dc79b6bcd-64kcm.default     Inbound   10.10.2.15:9080  10.10.2.19:56306 ratings.default.svc.cluster.local HBONE
reviews-v2-556d6457d-4r8n8.default      Outbound  10.10.2.18:45530 10.10.2.15:15008 10.200.1.129:9080                 HBONE
reviews-v3-564544b4d6-nmf92.default     Outbound  10.10.2.19:56168 10.10.2.15:15008 10.200.1.129:9080                 HBONE

docker exec -it myk8s-control-plane istioctl ztunnel-config connections --node myk8s-worker -o json
...
   {
        "state": "Up",
        "connections": {
            "inbound": [
                {
                    "src": "10.10.2.18:56440",
                    "originalDst": "ratings.default.svc.cluster.local",
                    "actualDst": "10.10.2.15:9080",
                    "protocol": "HBONE"
                },
                {
                    "src": "10.10.2.19:56306",
                    "originalDst": "ratings.default.svc.cluster.local",
                    "actualDst": "10.10.2.15:9080",
                    "protocol": "HBONE"
                }
            ],
            "outbound": []
        },
        "info": {
            "name": "ratings-v1-5dc79b6bcd-64kcm",
            "namespace": "default",
            "trustDomain": "",
            "serviceAccount": "bookinfo-ratings"
        }
    },
...

#
docker exec -it myk8s-control-plane istioctl ztunnel-config policy   
NAMESPACE POLICY NAME ACTION SCOPE

#
docker exec -it myk8s-control-plane istioctl ztunnel-config log   
ztunnel-25hpt.istio-system:
current log level is hickory_server::server::server_future=off,info
...

 

 

Secure Application Access : L4 Authorization Policy - Docs

Istio의 앰비언트 모드에서는 ztunnel을 통해 L4 보안 정책을 지원하며 호환 CNI 플러그인 기반 쿠버네티스 네트워크 정책과의 병행 사용이 가능하고, ztunnel과 웨이포인트 프록시의 계층화 구조를 통해 워크로드 단위로 L7 처리 여부를 선택적으로 적용할 수 있으며 정책 시행 지점이 다중화되는 특성이 있다.

# netshoot 파드만 ambient mode 다시 참여
docker exec -it myk8s-control-plane istioctl ztunnel-config workload
kubectl label pod netshoot istio.io/dataplane-mode=ambient --overwrite
docker exec -it myk8s-control-plane istioctl ztunnel-config workload


# L4 Authorization Policy 신규 생성
# Explicitly allow the netshoot and gateway service accounts to call the productpage service:
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: productpage-viewer
  namespace: default
spec:
  selector:
    matchLabels:
      app: productpage
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - cluster.local/ns/default/sa/netshoot
EOF

# L4 Authorization Policy 생성 확인
kubectl get authorizationpolicy
NAME                 AGE
productpage-viewer   8s

# ztunnel 파드 로그 모니터링
kubectl logs ds/ztunnel -n istio-system -f | grep -E RBAC

# L4 Authorization Policy 동작 확인
## 차단 확인!
GWLB=$(kubectl get svc bookinfo-gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
while true; do docker exec -it mypc curl $GWLB/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done

## 허용 확인!
kubectl exec -it netshoot -- curl -sS productpage:9080/productpage | grep -i title
while true; do kubectl exec -it netshoot -- curl -sS productpage:9080/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done


# L4 Authorization Policy 업데이트
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: productpage-viewer
  namespace: default
spec:
  selector:
    matchLabels:
      app: productpage
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - cluster.local/ns/default/sa/netshoot
        - cluster.local/ns/default/sa/bookinfo-gateway-istio
EOF

kubectl logs ds/ztunnel -n istio-system -f | grep -E RBAC


# 허용 확인!
GWLB=$(kubectl get svc bookinfo-gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
while true; do docker exec -it mypc curl $GWLB/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done

 

netshoot POD에서 실행된 요청은 정상 접근되는것을 알 수 있다.

Configure waypoint proxies - Docs

Istio 앰비언트 모드는 **ztunnel(L4)**과 **웨이포인트 프록시(L7)**의 계층적 구조로 운영되며, ztunnel은 노드 단위 mTLS 암호화 및 L4 트래픽 관리를 담당하고 웨이포인트는 선택적 L7 기능(HTTP 라우팅/정책/메트릭)을 처리한다. 웨이포인트 프록시는 네임스페이스 단위로 배포되며 여러 워크로드에서 공유 가능해 사이드카 대비 90% 이상의 리소스 절감 효과를 제공한다. L7 기능 필요 시에만 웨이포인트를 활성화할 수 있어 점진적 도입이 가능하며, HTTP 기반 트래픽 관리/보안 정책/관측성이 필요한 경우에 한해 배포딘다. 정책 시행 지점이 목적지 측 웨이포인트로 이동해 중앙 집중식 정책 관리가 가능하며, HBONE 프로토콜을 통해 ztunnel-웨이포인트 간 트래픽을 안전하게 전달한다. 앰비언트 모드 전환 시 기존 사이드카 구성과의 혼용 운영이 가능하며, CNI 플러그인과의 호환성을 통해 쿠버네티스 네트워크 정책과의 병행 사용이 지원된다.

# istioctl can generate a Kubernetes Gateway resource for a waypoint proxy. 
# For example, to generate a waypoint proxy named waypoint for the default namespace that can process traffic for services in the namespace:
kubectl describe pod bookinfo-gateway-istio-6cbd9bcd49-6cphf | grep 'Service Account'
Service Account:  bookinfo-gateway-istio

# Generate a waypoint configuration as YAML
docker exec -it myk8s-control-plane istioctl waypoint generate -h

# --for string        Specify the traffic type [all none service workload] for the waypoint
istioctl waypoint generate --for service -n default
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  labels:
    istio.io/waypoint-for: service
  name: waypoint
  namespace: default
spec:
  gatewayClassName: istio-waypoint
  listeners:
  - name: mesh
    port: 15008
    protocol: HBONE

#
docker exec -it myk8s-control-plane istioctl waypoint apply -n default
✅ waypoint default/waypoint applied

kubectl get gateway 
kubectl get gateway waypoint -o yaml
...

#
kubectl get pod -l service.istio.io/canonical-name=waypoint -owide
NAME                      READY   STATUS    RESTARTS   AGE     IP           NODE           NOMINATED NODE   READINESS GATES
waypoint-66b59898-p7v5x   1/1     Running   0          2m15s   10.10.1.21   myk8s-worker   <none>           <none>

#
docker exec -it myk8s-control-plane istioctl waypoint list  
NAME         REVISION     PROGRAMMED
waypoint     default      True

docker exec -it myk8s-control-plane istioctl waypoint status
NAMESPACE     NAME         STATUS     TYPE           REASON         MESSAGE
default       waypoint     True       Programmed     Programmed     Resource programmed, assigned to service(s) waypoint.default.svc.cluster.local:15008

docker exec -it myk8s-control-plane istioctl proxy-status   
NAME                                                CLUSTER        CDS               LDS               EDS               RDS               ECDS        ISTIOD                     VERSION
bookinfo-gateway-istio-6cbd9bcd49-6cphf.default     Kubernetes     SYNCED (5m5s)     SYNCED (5m5s)     SYNCED (5m4s)     SYNCED (5m5s)     IGNORED     istiod-86b6b7ff7-gmtdw     1.26.0
waypoint-66b59898-p7v5x.default                     Kubernetes     SYNCED (5m4s)     SYNCED (5m4s)     IGNORED           IGNORED           IGNORED     istiod-86b6b7ff7-gmtdw     1.26.0
ztunnel-52d22.istio-system                          Kubernetes     IGNORED           IGNORED           IGNORED           IGNORED           IGNORED     istiod-86b6b7ff7-gmtdw     1.26.0
ztunnel-ltckp.istio-system                          Kubernetes     IGNORED           IGNORED           IGNORED           IGNORED           IGNORED     istiod-86b6b7ff7-gmtdw     1.26.0
ztunnel-mg4mn.istio-system                          Kubernetes     IGNORED           IGNORED           IGNORED           IGNORED           IGNORED     istiod-86b6b7ff7-gmtdw     1.26.0

docker exec -it myk8s-control-plane istioctl proxy-config secret deploy/waypoint                              
RESOURCE NAME     TYPE           STATUS     VALID CERT     SERIAL NUMBER                        NOT AFTER                NOT BEFORE
default           Cert Chain     ACTIVE     true           4346296f183e559a876c0410cb154f50     2025-06-02T10:11:45Z     2025-06-01T10:09:45Z
ROOTCA            CA             ACTIVE     true           aa779aa04b241aedaa8579c0bc5c3b5f     2035-05-30T09:19:54Z     2025-06-01T09:19:54Z

#
kubectl pexec waypoint-66b59898-dlrj4 -it -T -- bash
----------------------------------------------
ip -c a

curl -s http://localhost:15020/stats/prometheus

ss -tnlp
State           Recv-Q          Send-Q                   Local Address:Port                    Peer Address:Port         Process                                 
LISTEN          0               4096                         127.0.0.1:15000                        0.0.0.0:*             users:(("envoy",pid=18,fd=18))         
LISTEN          0               4096                           0.0.0.0:15008                        0.0.0.0:*             users:(("envoy",pid=18,fd=35))         
LISTEN          0               4096                           0.0.0.0:15008                        0.0.0.0:*             users:(("envoy",pid=18,fd=34))         
LISTEN          0               4096                           0.0.0.0:15021                        0.0.0.0:*             users:(("envoy",pid=18,fd=23))         
LISTEN          0               4096                           0.0.0.0:15021                        0.0.0.0:*             users:(("envoy",pid=18,fd=22))         
LISTEN          0               4096                           0.0.0.0:15090                        0.0.0.0:*             users:(("envoy",pid=18,fd=21))         
LISTEN          0               4096                           0.0.0.0:15090                        0.0.0.0:*             users:(("envoy",pid=18,fd=20))         
LISTEN          0               4096                                 *:15020                              *:*             users:(("pilot-agent",pid=1,fd=11))  

ss -tnp

ss -xnlp
ss -xnp

exit
----------------------------------------------

 

 

 

 

이로써 9주간의 istio 스터디가 끝났다.

가시다 님 및 운영진님들 고생하셨고 좋은 스터디 제공해주셔서 감사합니다.

 

 

13.4 DNS 프록시 이해하기 Demystifying the DNS proxy

가상머신에서 클러스터 서비스 호스트네임을 해석하려면 DNS 프록시의 동작 방식을 반드시 이해해야 한다.

  1. 클라이언트 DNS 쿼리 생성
    • 애플리케이션(예: webapp.istioinaction)이 호스트네임 해석을 요청한다.
  2. 운영체제 hosts 파일 확인
    • /etc/hosts에 정적 매핑이 없는 경우, 기본 DNS 해석기(예: systemd-resolved)로 전달된다.
  3. Iptables 리다이렉트
    • istio-agent가 설정한 iptables 규칙이 DNS 쿼리를 127.0.0.53:53 대신 **DNS 프록시(15053 포트)**로 강제 전송한다.
  4. DNS 프록시 내부 검색
    • NDS API로 동기화된 메시 내 서비스(쿠버네티스 서비스, ServiceEntry) 정보를 기반으로 호스트네임을 해석한다.
    • 클러스터 서비스인 경우, 해당 VIP 또는 엔드포인트 IP를 반환한다.
  5. 외부 DNS 폴백
    • 메시 내 서비스가 아닌 경우, resolv.conf의 네임서버(예: 8.8.8.8)로 쿼리를 전달한다.

운영상 주의사항

  • resolv.conf 설정:
    DNS 프록시 주소(127.0.0.1:15053)를 첫 번째 네임서버로 반드시 지정해야 한다.
  • 성능 이점:
    클러스터 서비스 해석 시 CoreDNS 부하 감소레이턴시 개선 효과를 제공한다.
  • 보안 강화:
    외부 DNS 서버 노출을 최소화해 DNS 스푸핑 공격 위험을 줄인다.

iptables 규칙이 DNS 쿼리를 systemd-resolverd(127.0.0.53) 대신 로컬 DNS 프록시(127.0.0.1:15053)로 반드시 리다이렉트하도록 설정한다.

# [forum-vm]
# Iptables 규칙 확인 : proxyConfig.proxyMetadata ISTIO_META_DNS_CAPTURE="true" 설정 시 아래 규칙 추가됨
iptables-save | grep 'to-ports 15053'
-A OUTPUT -d 127.0.0.53/32 -p udp -m udp --dport 53 -j REDIRECT --to-ports 15053
-A ISTIO_OUTPUT -d 127.0.0.53/32 -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 15053

 

# tcp, udp 를 127.0.0.1 에 port 15053 에서 이스티오 에이전트(pilot-agent)가 DNS 프록시 처리 확인
netstat -ltunp | egrep 'PID|15053'
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:15053         0.0.0.0:*               LISTEN      3195/pilot-agent
udp        0      0 127.0.0.1:15053         0.0.0.0:*                           3195/pilot-agent

# 해당 주소로 DNS 쿼리해보자
dig +short @localhost -p 15053 webapp.istioinaction
10.10.200.48

dig +short @localhost -p 15053 catalog.istioinaction
dig +short @localhost -p 15053 forum.forum-services


# (옵션)
dig +short @localhost -p 15053 www.daum.net
daum-4vdtymgd.kgslb.com.
121.53.105.193


# (옵션) coredns 쿼리 로그 확인
KUBE_EDITOR="nano" kubectl edit cm -n kube-system coredns
apiVersion: v1
data:
  Corefile: |
    .:53 {
        log
        errors
        health
...

# 설정 반영되는데 다소 시간 소요
kubectl logs -n kube-system -l k8s-app=kube-dns -f

DNS 쿼리는 iptables 규칙에 의해 15053 포트의 Istio DNS 프록시로 반드시 리다이렉트된다. DNS 프록시는 istiod로부터 동기화된 메시 내 서비스 정보를 활용해 **FQDN(예: webapp.istioinaction)**을 즉시 해석한다. 외부 호스트네임은 resolv.conf에 정의된 업스트림 DNS로 자동 전달된다.

 

 

13.4.2 DNS 프록시가 인식하는 호스트네임은 무엇인가? Which hostnames is the DNS proxy aware of?

DNS 프록시는 istiod의 NDS API를 통해 클러스터 서비스의 FQDN 변형을 자동 생성하여 해석을 제공한다.

#
istioctl proxy-status | awk '{print $1}'
catalog-77fdb4997c-f8qj4.istioinaction
istio-eastwestgateway-86f6cb4699-4xfsn.istio-system
istio-ingressgateway-7b7ccd6454-pv8zp.istio-system
forum-vm.forum-services
webapp-684c568c59-vrj97.istioinaction

# NDS 설정을 가져올 때 proxyID 파라미터에 이름을 사용한다.
kubectl -n istio-system exec deploy/istiod -- curl -Ls "localhost:8080/debug/ndsz?proxyID=forum-vm.forum-services" | jq
{
  "resource": {
    "@type": "type.googleapis.com/istio.networking.nds.v1.NameTable",
    "table": {
      "catalog.istioinaction.svc.cluster.local": {
        "ips": [
          "10.10.200.138"
        ],
        "registry": "Kubernetes",
        "shortname": "catalog",
        "namespace": "istioinaction"
      },
      "forum.forum-services.svc.cluster.local": {
        "ips": [
          "10.10.200.72"
        ],
        "registry": "Kubernetes",
        "shortname": "forum",
        "namespace": "forum-services"
      },
      "webapp.istioinaction.svc.cluster.local": {
        "ips": [
          "10.10.200.48"
        ],
        "registry": "Kubernetes",
        "shortname": "webapp",
        "namespace": "istioinaction"
      },
...

 

  • webapp.istioinaction.svc.cluster.local과 같은 풀 네임 외에 webapp.istioinaction, webapp.istioinaction.svc 등 짧은 형식도 반드시 해석 가능하도록 변형을 생성한다.
  • 이는 쿠버네티스의 DNS 네임 컨벤션을 기반으로 자동화된다.
    • 동작 원리
      • istio-agent가 NDS 설정을 수신할 때 모든 가능한 FQDN 변형을 매핑 테이블에 추가한다.
      • 애플리케이션이 짧은 호스트명(예: webapp.istioinaction)을 사용해도 DNS 프록시가 정확한 클러스터 IP로 해석한다.

이를 통해 Istio는 다양한 네이밍 형식을 지원하며, 애플리케이션 수정 없이 메시 내 서비스 검색을 단순화한다.

 

핵심은 다음과 같다.

  • DNS 프록시는 istiod가 알고 있는 서비스들로 설정된다.
  • istio-agent는 호스트네임의 더 짧은 변형들을 생성한다 (쿠버네티스 내의 경험과 일치시키기 위함이다)
  • 이런 DNS 프록시 내의 레코드는 클러스터 내 서비스 호스트네임을 해석하는 데 사용된다.
  • 클러스터가 아닌 호스트네임(퍼블릭 도메인 같은)쿼리는 머신에서 처음 설정한 네임서버로 넘어간다.

 

 

13.5 에이전트 동작 커스터마이징하기 Customizing the agent’s behavior

DNS 프록시의 디버그 로깅 및 인증서 수명 조정을 위해 /var/lib/istio/envoy/sidecar.env 파일을 수정해야 하며, 변경 후 /var/log/istio/istio.log 확인과 /etc/certs/cert-chain.pem 갱신 여부를 검증해야 한다.

#
cat /var/lib/istio/envoy/sidecar.env
grep "^[^#]" /var/lib/istio/envoy/sidecar.env # 주석 처리

#
echo 'ISTIO_AGENT_FLAGS="--log_output_level=dns:debug"' >> /var/lib/istio/envoy/sidecar.env
echo 'SECRET_TTL="12h0m0s"' >> /var/lib/istio/envoy/sidecar.env
grep "^[^#]" /var/lib/istio/envoy/sidecar.env # 주석 처리
ISTIO_AGENT_FLAGS="--log_output_level=dns:debug"
SECRET_TTL="12h0m0s"

# 변경 사항 적용
systemctl restart istio


# www.daum.net 도메인은 질의 처리를 하지 못해서, 로컬에 resolver DNS 서버를 통해 질의 로그 확인.
tail -f /var/log/istio/istio.log
2025-05-25T07:23:03.660035Z	debug	dns	response for hostname "www.daum.net." not found in dns proxy, querying upstream
2025-05-25T07:23:03.662845Z	debug	dns	upstream response for hostname "www.daum.net." : ;; opcode: QUERY, status: NOERROR, id: 30399
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version 0; flags: ; udp: 65494

;; QUESTION SECTION:
;www.daum.net.	IN	 A

;; ANSWER SECTION:
www.daum.net.	77	IN	CNAME	daum-4vdtymgd.kgslb.com.
daum-4vdtymgd.kgslb.com.	5	IN	A	121.53.105.193

#
dig +short @localhost -p 15053 www.daum.net

 

13.6 메시에서 WorkloadEntry 제거하기 Removing a WorkloadEntry from the mesh

aws에서 forum VM을 강제로 삭제(terminated)한다.

 

 

 

지금까지는 이스티오 서비스 메시를 컨테이너 및 쿠버네티스 관점에서 다뤘다. 그러나 실제 워크로드는 자주 가상머신이나 물리 머신에서 실행된다. 엔터프라이즈는 규제 준수나 전문 지식 부족 등 다양한 이유로 워크로드를 온프레미스에서 실행해야 할 수 있다. 이번 장에서는 사이드카 프록시를 설치하고 설정함으로써 어떤 워크로드든 메시의 일부로 삼을 수 있는 방법을 보여준다. 이 접근법은 레거시 워크로드를 복원력 있고 안전하며 고가용성적인 방식으로 메시로 통합하길 원하는 엔터프라이즈에게 흥미로운 기능을 제공한다.

 

 

실습환경 구성

구성 : VPC 1개, EC2 인스턴스 1대 (Ubuntu 22.04 LTS, t3.xlarge - vCPU 4 , Mem 16) , forum-vm 1대는 t3.small

AWSTemplateFormatVersion: '2010-09-09'

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "<<<<< Deploy EC2 >>>>>"
        Parameters:
          - KeyName
          - SgIngressSshCidr
          - MyInstanceType
          - LatestAmiId

      - Label:
          default: "<<<<< Region AZ >>>>>"
        Parameters:
          - TargetRegion
          - AvailabilityZone1
          - AvailabilityZone2

      - Label:
          default: "<<<<< VPC Subnet >>>>>"
        Parameters:
          - VpcBlock
          - PublicSubnet1Block
          - PublicSubnet2Block

Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instances.
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  SgIngressSshCidr:
    Description: The IP address range that can be used to communicate to the EC2 instances.
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 0.0.0.0/0
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  MyInstanceType:
    Description: Enter EC2 Type(Spec) Ex) t2.micro.
    Type: String
    Default: t3.xlarge
  LatestAmiId:
    Description: (DO NOT CHANGE)
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id'
    AllowedValues:
      - /aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id

  TargetRegion:
    Type: String
    Default: ap-northeast-2
  AvailabilityZone1:
    Type: String
    Default: ap-northeast-2a
  AvailabilityZone2:
    Type: String
    Default: ap-northeast-2c

  VpcBlock:
    Type: String
    Default: 192.168.0.0/16
  PublicSubnet1Block:
    Type: String
    Default: 192.168.10.0/24
  PublicSubnet2Block:
    Type: String
    Default: 192.168.20.0/24

Resources:
# VPC
  IstioVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: Istio-VPC

# PublicSubnets
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Ref AvailabilityZone1
      CidrBlock: !Ref PublicSubnet1Block
      VpcId: !Ref IstioVPC
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Istio-PublicSubnet1
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Ref AvailabilityZone2
      CidrBlock: !Ref PublicSubnet2Block
      VpcId: !Ref IstioVPC
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Istio-PublicSubnet2

  InternetGateway:
    Type: AWS::EC2::InternetGateway
  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref IstioVPC

  PublicSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref IstioVPC
      Tags:
        - Key: Name
          Value: Istio-PublicSubnetRouteTable
  PublicSubnetRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicSubnetRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicSubnetRouteTable
  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicSubnetRouteTable

# EC2 Hosts
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Kans EC2 Security Group
      VpcId: !Ref IstioVPC
      Tags:
        - Key: Name
          Value: Istio-SG
      SecurityGroupIngress:
      - IpProtocol: '-1'
        CidrIp: !Ref SgIngressSshCidr
      - IpProtocol: '-1'
        CidrIp: !Ref VpcBlock
      - IpProtocol: '-1'
        CidrIp: 10.10.200.0/24
      - IpProtocol: '-1'
        CidrIp: 172.16.0.0/16
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 8080
        ToPort: 8080
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 30000
        ToPort: 30000
        CidrIp: 0.0.0.0/0

  EC21:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref MyInstanceType
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyName
      Tags:
        - Key: Name
          Value: k3s-s
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref PublicSubnet1
          GroupSet:
          - !Ref EC2SG
          AssociatePublicIpAddress: true
          PrivateIpAddress: 192.168.10.10
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp3
            VolumeSize: 30
            DeleteOnTermination: true
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash
            hostnamectl --static set-hostname k3s-s

            # Config convenience
            echo 'alias vi=vim' >> /etc/profile
            echo "sudo su -" >> /home/ubuntu/.bashrc
            ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

            # Disable ufw & apparmor 
            systemctl stop ufw && systemctl disable ufw
            systemctl stop apparmor && systemctl disable apparmor

            # Install packages
            apt update && apt-get install bridge-utils net-tools conntrack ngrep jq tree unzip kubecolor -y

            # local dns - hosts file
            echo "192.168.10.10 k3s-s" >> /etc/hosts

            # Install k3s-server
            curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.28.15+k3s1 INSTALL_K3S_EXEC=" --disable=traefik"  sh -s - server --token istiotoken --cluster-cidr "172.16.0.0/16" --service-cidr "10.10.200.0/24" --write-kubeconfig-mode 644 

            # Change kubeconfig
            echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> /etc/profile

            # Install Helm
            curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

            # Alias kubectl to k
            echo 'alias kc=kubecolor' >> /etc/profile
            echo 'alias k=kubectl' >> /etc/profile
            echo 'complete -o default -F __start_kubectl k' >> /etc/profile

            # kubectl Source the completion
            source <(kubectl completion bash)
            echo 'source <(kubectl completion bash)' >> /etc/profile
            
            # Install Kubectx & Kubens
            git clone https://github.com/ahmetb/kubectx /opt/kubectx
            ln -s /opt/kubectx/kubens /usr/local/bin/kubens
            ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx

            # Install Kubeps & Setting PS1
            git clone https://github.com/jonmosco/kube-ps1.git /root/kube-ps1
            cat <<"EOT" >> ~/.bash_profile
            source /root/kube-ps1/kube-ps1.sh
            KUBE_PS1_SYMBOL_ENABLE=true
            function get_cluster_short() {
              echo "$1" | cut -d . -f1
            }
            KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
            KUBE_PS1_SUFFIX=') '
            PS1='$(kube_ps1)'$PS1
            EOT


  EC24:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.small
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyName
      Tags:
        - Key: Name
          Value: forum-vm
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref PublicSubnet1
          GroupSet:
          - !Ref EC2SG
          AssociatePublicIpAddress: true
          PrivateIpAddress: 192.168.10.200
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp3
            VolumeSize: 30
            DeleteOnTermination: true
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash
            hostnamectl --static set-hostname forum-vm

            # Config convenience
            echo 'alias vi=vim' >> /etc/profile
            echo "sudo su -" >> /home/ubuntu/.bashrc
            ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

            # Disable ufw & apparmor 
            systemctl stop ufw && systemctl disable ufw
            systemctl stop apparmor && systemctl disable apparmor

            # Install packages
            apt update && apt-get install net-tools ngrep jq tree unzip apache2 -y

            # local dns - hosts file
            echo "192.168.10.200  forum-vm" >> /etc/hosts


Outputs:
  Serverhost:
    Value: !GetAtt EC21.PublicIp

 

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/istio-8w.yaml

# CloudFormation 스택 배포
# aws cloudformation deploy --template-file kans-7w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file istio-8w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

## Tip. 인스턴스 타입 변경 : MyInstanceType=t3.xlarge (vCPU 4, Mem 16)
예시) aws cloudformation deploy --template-file istio-8w.yaml --stack-name mylab --parameter-overrides MyInstanceType=t3.xlarge KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2

# [모니터링] CloudFormation 스택 상태 : 생성 완료 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

# 배포된 aws ec2 유동 공인 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   43.202.60.44    running
testpc  15.165.15.104   running

# EC2 SSH 접속 : 바로 접속하지 말고, 3~5분 정도 후에 접속 할 것
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
...
(⎈|default:N/A) root@k3s-s:~# <- kubeps 가 나오지 않을 경우 ssh logout 후 다시 ssh 접속 할 것!

 

k3s-s , forum-vm 각각 접속 후 확인

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.201.48.21    running
testpc  15.63.11.32   running

#k3s-s 접속 후 확인 : ssh -i <> ubuntu@3.201.48.21
kc get node -owide
NAME     STATUS   ROLES                  AGE     VERSION         INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION   CONTAINER-RUNTIME
k3s-s    Ready    control-plane,master   2m12s   v1.28.15+k3s1   192.168.10.10    <none>        Ubuntu 22.04.5 LTS   6.8.0-1029-aws   containerd://1.7.22-k3s1.28

hostnamectl 
...
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.xlarge


# (옵션) krew 설치
wget -O /root/krew-linux_amd64.tar.gz https://github.com/kubernetes-sigs/krew/releases/download/v0.4.4/krew-linux_amd64.tar.gz
tar zxvf /root/krew-linux_amd64.tar.gz
/root/krew-linux_amd64 install krew
export PATH="$PATH:/root/.krew/bin"
echo 'export PATH="$PATH:/root/.krew/bin:/root/go/bin"' >> /etc/profile
kubectl krew install get-all neat view-secret rolesum pexec


# (옵션) termshark 설치
apt install termshark -y # 대화형창에서 yes 입력 => DEBIAN_FRONTEND=noninteractive 
tshark -D
termshark -v




#forum-vm 접속 후 확인 : ssh -i <> ubuntu@15.63.11.32
ip -br -c addr
ip -c a

hostnamectl 
...
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.small
  
# (옵션) termshark 설치
apt install termshark -y # 대화형창에서 yes 입력
tshark -D
termshark -v

 

 

13.1 이스티오의 가상머신 지원

이스티오의 가상머신 지원은 초기부터 가능했으나 제어 평면 외부의 복잡한 설정이 필요했고, 1.9.0 버전에서 WorkloadGroup, WorkloadEntry 리소스 도입 및 DNS 해석 기능 강화를 통해 베타 단계로 진화했다. 가상머신 통합을 위해 istioctl로 간소화된 사이드카 프록시 설치, 고가용성 보장, 메시 내 서비스 DNS 해석 등 3가지 핵심 기능이 구현됐다. 

 

가상머신을 이스티오 메시에 통합하려면 사이드카 프록시를 설치해야 하며, 프록시가 istiod와 연결되도록 설정해야 한다. 또한 ID 토큰을 부여해 메시 인증을 반드시 완료해야 한다. 쿠버네티스에서는 웹훅/istioctl로 자동화되지만, 가상머신은 수동 구성이 필수적이다.

단일 네트워크 환경에서는 VM과 쿠버네티스 클러스터가 동일 L3 네트워크를 공유하므로 직접 통신이 가능하며, WorkloadGroup 템플릿을 통해 자동 등록(WorkloadEntry 생성)이 가능하다. 멀티 네트워크 환경에서는 이스트-웨스트 게이트웨이를 통해 네트워크를 연결해야 하며, 모든 트래픽(제어 플레인/데이터 플레인)이 게이트웨이를 경유하도록 설정이 필요하다.

이스티오 1.9 버전부터 WorkloadGroup, WorkloadEntry 리소스와 DNS 프록시 기능이 도입되어 가상머신 통합 프로세스가 단순화됐다.

 

가상머신에 ID를 프로비저닝하려면 쿠버네티스 서비스 어카운트 토큰을 수동으로 생성 및 전달해야 한다. 쿠버네티스 파드는 자동 토큰 주입이 가능하지만, 가상머신은 istio-agent가 토큰을 사용해 istiod에 인증하는 과정을 반드시 수동으로 구성해야 한다.

단점으로는 멀티 클라우드 환경에서 토큰 생성/전달 프로세스의 반복적 작업 부담이 발생하며, 플랫폼 할당 ID (예: AWS IAM 역할)를 활용한 자동화 솔루션이 현재 개발 중이다. 이스티오 커뮤니티는 클라우드 프로바이더의 신뢰 체계를 활용해 VM 인증을 단순화하는 방안을 제시하고 있다.

현재 예제에서는 실습 편의를 위해 쿠버네티스를 신뢰 원천으로 사용하며, 토큰 전달을 수동으로 진행한다. 향후 플랫폼 기반 ID 통합이 완료되면 운영 효율성이 크게 개선될 전망이다.

 

 

가상머신의 고가용성 달성을 위해 WorkloadGroupWorkloadEntry 리소스를 반드시 활용해야 한다.

  1. WorkloadGroup은 쿠버네티스의 디플로이먼트와 유사하게 포트 설정, 레이블, 서비스 어카운트, 상태 프로브 방법 등 공통 속성을 정의한다.
  2. WorkloadEntry는 개별 가상머신 인스턴스를 나타내며, 인스턴스 상태주소 같은 고유 속성을 관리한다. 쿠버네티스의 파드와 동일한 역할을 수행한다.
  3. 수동으로 WorkloadEntry를 생성할 수 있지만, **워크로드 자동 등록(auto-registration)**을 통해 신규 VM이 메시에 자동 통합되도록 구성하는 것이 권장된다. 이를 통해 인스턴스 확장/축소 시 가용성을 유지할 수 있다.

이스티오는 쿠버네티스의 고가용성 메커니즘을 차용해 VM 워크로드의 운영 효율성을 높인다.

워크로드 자동 등록을 구현하려면 WorkloadGroup을 사전에 정의해야 하며, 가상머신이 ID 토큰으로 인증해 WorkloadEntry를 자동 생성하는 과정이 필수적이다.

  1. 트래픽 라우팅
    쿠버네티스 서비스나 ServiceEntry가 레이블 셀렉터를 통해 WorkloadEntry를 백엔드로 선택해야 한다. 이를 통해 VM과 파드를 동일한 서비스 엔드포인트로 통합 관리할 수 있다.
  2. 마이그레이션 전략
    레거시 VM과 현대화 파드를 병렬 운영하면서 트래픽 전환 기능(예: 카나리아 배포)을 적용해야 한다. 오류 발생 시 VM으로 트래픽을 즉시 되돌리는 롤백 메커니즘이 반드시 필요하다.
  3. 운영 효율성
    워크로드 상태에 따라 자동으로 인스턴스를 확장/축소할 수 있어 고가용성을 보장한다. 클라이언트 측 변경 없이 인프라 변경이 가능하다는 장점이 있다.

 

서비스 메시에서 readiness 프로브는 반드시 이스티오가 관리하여 워크로드의 트래픽 처리 준비 상태를 확인해야 한다. liveness 프로브는 플랫폼(예: 쿠버네티스, AWS/Azure/GCP)의 책임이며, 애플리케이션 건강 상태를 모니터링하고 장애 시 인스턴스 자동 복구를 수행해야 한다.

  • Readiness 프로브: 이스티오가 트래픽 수신 가능 여부를 검사해 메시 내 통합을 제어한다.
  • Liveness 프로브: 클라우드 플랫폼별 자동 복구 기능(예: AWS Auto Scaling, Azure VM 복구)을 반드시 활용해야 한다.

따라서 가상머신 운영 시 플랫폼의 헬스 체크 및 복구 메커니즘을 필수적으로 구성해야 한다.

가장 인기 있는 세 클라우드 프로바이더의 liveness 검사 및 자동 복구

이스티오의 가상머신 Readiness 프로브 구현 방식

가상머신의 readiness 프로브WorkloadGroup에 정의된 설정에 따라 istio-agent가 주기적으로 애플리케이션 상태를 검사해야 한다. 검사 결과는 istiod에 보고되며, 정상 상태일 때만 트래픽을 수신하도록 데이터 플레인을 구성한다.

핵심 메커니즘

  1. 상태 보고 및 라우팅 제어
  • istio-agent는 애플리케이션 헬스 체크 결과를 istiod에 전달해야 한다.
  • 비정상 상태 감지 시 해당 VM 엔드포인트를 데이터 플레인에서 즉시 제거해야 한다.
  1. 프로브 구성 권장 사항
  • Readiness 프로브:
    • 공격적 설정 적용이 필수적이다(예: 5초 주기 체크, 2회 연속 실패 시 제외).
    • 오류 발생 시 트래픽 유입을 즉시 차단해야 한다.
  • Liveness 프로브:
    • 클라우드 플랫폼(AWS/Azure/GCP)의 자동 복구 기능을 반드시 활용해야 한다.
    • 보수적 설정 적용(예: 30초 주기 체크, 3회 연속 실패 후 인스턴스 재생성).

운영 주의사항

  • 유예 기간 고려:
    인스턴스 종료 전 진행 중인 요청 완료를 위해 grace period를 설정해야 한다.
  • 실패 순서 관리:
    Readiness 프로브가 Liveness 프로브보다 먼저 실패하도록 설계해 불필요한 인스턴스 재생성을 방지해야 한다.

이스티오는 쿠버네티스의 헬스 체크 메커니즘을 확장해 VM 환경에 적용하며, 운영자 반드시 인프라 계층과의 연동을 검증해야 한다.

 

가상머신이 메시 내 서비스의 DNS를 해석하려면 로컬 DNS 프록시를 반드시 활용해야 한다. 쿠버네티스 외부 VM은 클러스터 내부 DNS에 접근할 수 없어 istio-agent에 내장된 DNS 프록시로 트래픽을 리다이렉트해야 한다.

  1. DNS 쿼리 처리 메커니즘
  • 애플리케이션의 DNS 요청은 iptables 규칙을 통해 DNS 프록시로 전달해야 한다.
  • DNS 프록시는 istiod가 제공하는 메시 서비스 정보(쿠버네티스 서비스, ServiceEntry)로 동적으로 업데이트되어야 한다.
  1. NDS(Name Discovery Service) API
  • 서비스 추가/변경 시 NDS API를 통해 DNS 프록시에 실시간으로 변경 사항을 반영해야 한다.
  • 이를 통해 외부 DNS 서버(external-dns) 의존성을 제거하고 통합 관리가 가능하다.
  1. 장점
  • 멀티 네트워크 환경에서도 DNS 해석이 자동화되어 VM-파드 간 원활한 통신을 보장해야 한다.
  • DNS 프록시는 TCP/UDP 트래픽 캡처 기능과 결합되어 애플리케이션 수정 없이 트래픽 제어가 가능하다.

이스티오 1.8 이상에서는 DNS 프록시가 기본 구성 요소로 포함되며, 운영자는 VM의 /etc/resolv.conf 설정을 DNS 프록시(127.0.0.1:15053)로 변경해야 한다.

 

Istio DNS Proxying은 DNS 라운드트립을 반드시 제거해야 응답 속도 개선과 CoreDNS 부하 감소를 동시에 달성할 수 있다.

  1. 트래픽 처리 방식
  • 메시 내 Kubernetes 서비스, Pod, ServiceEntry 정보를 미리 동기화해 애플리케이션 변경 없이 DNS 해석을 수행해야 한다.
  • 외부 서비스(예: AWS RDS)는 ServiceEntry 등록을 통해 VIP(Class E 대역)를 자동 할당해야 하며, 이를 통해 포트 충돌 없이 리스너를 구성해야 한다.
  1. 운영 효율성
  • DNS 서버 변경 없이 새 도메인 추가가 가능하도록 ServiceEntry에 도메인/IP 정보를 반드시 등록해야 한다.
  • 외부 TCP 서비스 통합 시 VIP 자동 할당 기능을 활용해 복수 엔드포인트 관리 효율성을 높여야 한다.

이 방식은 애플리케이션 레벨 수정 없이 DNS 해석과 트래픽 라우팅을 통합 관리할 수 있는 이스티오의 핵심 장점이다.

 

 

13.2 인프라 준비하기 Setting up the infrastructure

# (옵션) kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

## kube-ops-view 접속 URL 확인
echo -e "http://$(curl -s ipinfo.io/ip):30007/#scale=1.5"
echo -e "http://$(curl -s ipinfo.io/ip):30007/#scale=1.3"

# 소스코드 clone
git clone https://github.com/AcornPublishing/istio-in-action
tree istio-in-action/book-source-code-master -L 1

# istioctl 설치
export ISTIOV=1.17.8
echo 'export ISTIOV=1.17.8' >> /root/.bashrc

curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
istioctl version --remote=false

# 클러스터와 가상머신이 다른 네트워크에 있으므로, 이스티오를 설치한 네임스페이스에 네트워크 정보 레이블을 지정해야 한다.
kubectl create namespace istio-system
kubectl label namespace istio-system topology.istio.io/network=west-network

# demo 프로파일 컨트롤 플레인 배포
cat istio-in-action/book-source-code-master/ch13/controlplane/cluster-in-west-network.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-controlplane
  namespace: istio-system
spec:
  profile: demo
  components:
    egressGateways:
    - name: istio-egressgateway
      enabled: false
  values:
    global:
      meshID: usmesh
      multiCluster:
        clusterName: west-cluster
      network: west-network

istioctl install -f istio-in-action/book-source-code-master/ch13/controlplane/cluster-in-west-network.yaml --set values.global.proxy.privileged=true -y


# 보조 도구 설치
kubectl apply -f istio-$ISTIOV/samples/addons


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

# 실습을 위한 네임스페이스 설정
kubectl create ns istioinaction
kubectl label namespace istioinaction istio-injection=enabled
kubectl get ns --show-labels

# istio-ingressgateway 서비스 : NodePort 변경 및 nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30005}]}}'
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
kc describe svc -n istio-system istio-ingressgateway

# NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'

# Prometheus 접속 : envoy, istio 메트릭 확인
echo -e "http://$(curl -s ipinfo.io/ip):30001"

# Grafana 접속
echo -e "http://$(curl -s ipinfo.io/ip):30002"

# Kiali 접속 : NodePort
echo -e "http://$(curl -s ipinfo.io/ip):30003"

# tracing 접속 : 예거 트레이싱 대시보드
echo -e "http://$(curl -s ipinfo.io/ip):30004"

 

 

cool-store 서비스/gw/vs 배포 및 http 요청 확인

#cool-store 서비스/gw/vs 배포 및 http 요청 확인
# cool-store 서비스/gw/vs 배포
kubectl -n istioinaction apply -f istio-in-action/book-source-code-master/ch12/webapp-deployment-svc.yaml
kubectl -n istioinaction apply -f istio-in-action/book-source-code-master/ch12/webapp-gw-vs.yaml
kubectl -n istioinaction apply -f istio-in-action/book-source-code-master/ch12/catalog.yaml

# 확인
kc get deploy,svc -n istioinaction
kc get gw,vs -n istioinaction



#http 요청 확인
# k3s-s
curl -s -H "Host: webapp.istioinaction.io" http://192.168.10.10:30000/api/catalog/ | jq
curl -s -H "Host: webapp.istioinaction.io" http://192.168.10.10:30000/api/catalog/items/1 | jq

# [forum-vm] k8s cool-store 요청 시도 확인
APP_IP=43.202.64.8 # k8s-s 의 공인 IP
curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/catalog/items/1 | jq
while true; do curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/catalog/ ; echo; date; sleep 1; done

# [자신의 PC] k8s cool-store 요청 시도 확인
바로 위 [testpc]와 동일 요청

# [k8s-s] forum-vm web 요청 시도 확인
curl 192.168.10.200
curl 192.168.10.200 -I

VM_IP=15.165.15.104 # testpc 의 공인 IP
curl $VM_IP
curl $VM_IP -I

 

13.3 가상머신까지 메시 확인

가상머신 메시 통합 기능은 현재 베타 단계이며, 기본 비활성화 상태이므로 IstioOperator CRD를 통해 반드시 명시적으로 활성화해야 한다.

필수 활성화 항목

  • 워크로드 자동 등록: WorkloadGroup/WorkloadEntry 자동 생성
  • 헬스 체크: readiness/liveness 프로브 관리
  • DNS 프록시: 클러스터 내 서비스 DNS 해석 자동화

베타 단계 특성

  • 기능 안정성은 검증됐으나 향후 API 변경 가능성이 존재한다.
  • 운영 환경 적용 시 Istio 1.9+ 버전 필수이며, 주기적인 컨트롤 플레인 업데이트가 권장된다.

이 설정은 메시 확장성을 위해 반드시 구현해야 하는 핵심 요소로, 온프레미스/멀티클라우드 환경에서 레거시 워크로드 통합에 적합하다.

# 컨트롤 플레인 업데이트 Mesh expansion to VMs
cat istio-in-action/book-source-code-master/ch13/controlplane/cluster-in-west-network-with-vm-features.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-controlplane
  namespace: istio-system
spec:
  profile: demo
  components:
    egressGateways:
    - name: istio-egressgateway
      enabled: false
  meshConfig:
    defaultConfig:
      proxyMetadata:
        ISTIO_META_DNS_CAPTURE: "true" # DNS 쿼리가 캡처돼 DNS 프록시로 리다이렉트된다
  values:
    pilot:
      env:
        PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION: true # 워크로드를 컨트롤 플레인에 자동 등록할 수 있다
        PILOT_ENABLE_WORKLOAD_ENTRY_HEALTHCHECKS: true # 가상머신 워크로드의 상태를 검사한다
    global:
      meshID: usmesh
      multiCluster:
        clusterName: west-cluster
      network: west-network

istioctl install -f istio-in-action/book-source-code-master/ch13/controlplane/cluster-in-west-network-with-vm-features.yaml --set values.global.proxy.privileged=true -y
kubectl patch svc istio-ingressgateway -n istio-system -p '{"spec": {"type": "NodePort"}}'

업데이트된 컨트롤 플레인은 DNS 쿼리 리다이렉션, 워크로드 자동 등록, 헬스 체크 기능을 제공하며, 가상머신이 istiod와 연결되어 설정 및 ID를 반드시 수신해야 한다.

 

13.3.1 istiod와 클러스터 서비스들은 가상머신에 노출하기* Exposing istiod and cluster services to the VM

가상머신이 멀티 네트워크 환경에서 메시에 통합되려면 이스트-웨스트 게이트웨이 설치가 반드시 필요하다. 이 게이트웨이는 **mTLS 포트(15443)**를 노출해 VM과 istiod/클러스터 서비스 간 통신을 프록시해야 한다.

#
cat istio-in-action/book-source-code-master/ch13/gateways/cluster-east-west-gw.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-eastwestgateway
  namespace: istio-system
spec:
  profile: empty
  components:
    ingressGateways:
    - name: istio-eastwestgateway
      label:
        istio: eastwestgateway
        app: istio-eastwestgateway
        topology.istio.io/network: west-network
      enabled: true
      k8s:
        env:
        - name: ISTIO_META_ROUTER_MODE
          value: "sni-dnat"
        # The network to which traffic is routed
        - name: ISTIO_META_REQUESTED_NETWORK_VIEW
          value: west-network
        service:
          ports:
          - name: status-port
            port: 15021
            targetPort: 15021
          - name: mtls
            port: 15443
            targetPort: 15443
          - name: tcp-istiod
            port: 15012
            targetPort: 15012
          - name: tcp-webhook
            port: 15017
            targetPort: 15017  
  values:
    global:
      meshID: usmesh
      multiCluster:
        clusterName: west-cluster
      network: west-network

#
istioctl install -f istio-in-action/book-source-code-master/ch13/gateways/cluster-east-west-gw.yaml -y

#
kubectl get pod -n istio-system -l chart=gateways
NAME                                     READY   STATUS    RESTARTS   AGE
istio-eastwestgateway-86f6cb4699-nbhnp   1/1     Running   0          9m47s
istio-ingressgateway-7b7ccd6454-xmqgw    1/1     Running   0          22m

kubectl get svc -n istio-system -l istio.io/rev=default
NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                                                                      AGE
istio-eastwestgateway   LoadBalancer   10.10.200.252   192.168.10.10   15021:32278/TCP,15443:31099/TCP,15012:31437/TCP,15017:32573/TCP              178m
istio-ingressgateway    NodePort       10.10.200.125   <none>          15021:31538/TCP,80:30000/TCP,443:30005/TCP,31400:31525/TCP,15443:30143/TCP   4h1m
istiod                  ClusterIP      10.10.200.111   <none>          15010/TCP,15012/TCP,443/TCP,15014/TCP                                        4h1m

kubectl get svc -n istio-system istio-eastwestgateway -o json | jq
...
      {
        "name": "status-port",
        "nodePort": 32278,
        "port": 15021,
        "protocol": "TCP",
        "targetPort": 15021
      },
      {
        "name": "mtls",
        "nodePort": 31099,
        "port": 15443,
        "protocol": "TCP",
        "targetPort": 15443
      },
      {
        "name": "tcp-istiod",
        "nodePort": 31437,
        "port": 15012,
        "protocol": "TCP",
        "targetPort": 15012
      },
      {
        "name": "tcp-webhook",
        "nodePort": 32573,
        "port": 15017,
        "protocol": "TCP",
        "targetPort": 15017
      }
...

kubectl get svc -n istio-system istiod -o json | jq
...
      {
        "name": "https-dns",
        "port": 15012,
        "protocol": "TCP",
        "targetPort": 15012
      },
      {
        "name": "https-webhook",
        "port": 443,
        "protocol": "TCP",
        "targetPort": 15017
      },
...

 

  1. 네트워크 분리 시:
    • VM ↔ istiod/서비스 직접 통신 불가 → 게이트웨이를 통한 트래픽 프록시 필수
  2. 포트 노출:
    • 15443 포트: 메시 내 서비스로의 역방향 프록시 담당
    • 15012/TCP(istiod XDS), 15017/TCP(인증 웹훅) 추가 노출 권장
     
#
cat istio-in-action/book-source-code-master/ch13/expose-services.yaml 
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: cross-network-gateway
  namespace: istio-system
spec:
  selector:
    istio: eastwestgateway
  servers:
    - port:
        number: 15443
        name: tls
        protocol: TLS
      tls:
        mode: AUTO_PASSTHROUGH
      hosts:
        - "*.local"

kubectl apply -f istio-in-action/book-source-code-master/ch13/expose-services.yaml 
kubectl get gw,vs -A
NAMESPACE       NAME                                                AGE
istio-system    gateway.networking.istio.io/cross-network-gateway   2m23s
istioinaction   gateway.networking.istio.io/coolstore-gateway       82m

NAMESPACE       NAME                                                       GATEWAYS                HOSTS                         AGE
istioinaction   virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   82m
#
cat istio-in-action/book-source-code-master/ch13/expose-istiod.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: istiod-gateway
spec:
  selector:
    istio: eastwestgateway
  servers:
    - port:
        name: tls-istiod
        number: 15012
        protocol: tls
      tls:
        mode: PASSTHROUGH        
      hosts:
        - "*"
    - port:
        name: tls-istiodwebhook
        number: 15017
        protocol: tls
      tls:
        mode: PASSTHROUGH          
      hosts:
        - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: istiod-vs
spec:
  hosts:
  - "*"
  gateways:
  - istiod-gateway
  tls:
  - match:
    - port: 15012
      sniHosts:
      - "*"
    route:
    - destination:
        host: istiod.istio-system.svc.cluster.local
        port:
          number: 15012
  - match:
    - port: 15017
      sniHosts:
      - "*"
    route:
    - destination:
        host: istiod.istio-system.svc.cluster.local
        port:
          number: 443

# 
kubectl apply -f istio-in-action/book-source-code-master/ch13/expose-istiod.yaml -n istio-system


# 13.3.2 실습 전 아래와 같이 gw,vs 리소스가 생성되어있는지 확인하자!
kc get gw,vs -A
NAMESPACE       NAME                                                AGE
istio-system    gateway.networking.istio.io/istiod-gateway          9s
istio-system    gateway.networking.istio.io/cross-network-gateway   4m40s
istioinaction   gateway.networking.istio.io/coolstore-gateway       84m

NAMESPACE       NAME                                                       GATEWAYS                HOSTS                         AGE
istio-system    virtualservice.networking.istio.io/istiod-vs               ["istiod-gateway"]      ["*"]                         9s
istioinaction   virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   84m

 

  • WorkloadGroup 생성: VM 그룹의 공통 속성(포트, 레이블, 헬스 체크) 정의 필요
  • 자동 등록 구성: VM 시작 시 WorkloadEntry 자동 생성되도록 설정

이 과정을 통해 멀티 네트워크 환경에서도 VM을 메시에 안정적으로 통합할 수 있다.

 

13.3.2 WorkloadGroup 으로 워크로드 그룹 나타내기* Representing a group of workloads with a WorkloadGroup

워크로드 그룹(WorkloadGroup)은 가상머신 집합의 공통 속성을 반드시 정의해야 한다.

  1. 레이블(labels): 쿠버네티스 서비스가 워크로드 엔트리를 선택하기 위한 기준으로 활용해야 한다.
  2. 네트워크(network): 동일 네트워크 시 IP 직접 접근, 다른 네트워크 시 이스트-웨스트 게이트웨이를 통해 트래픽을 라우팅해야 한다.
  3. 서비스 어카운트(serviceAccount): 워크로드 식별을 위해 반드시 지정해야 하며, VM은 해당 토큰으로 인증을 완료해야 한다.
  4. 헬스 체크(probe): HTTP GET 방식을 사용해 애플리케이션 준비 상태를 주기적으로 검증해야 한다(예: 5초 주기, /api/healthz 경로).
#
cat istio-in-action/book-source-code-master/ch13/workloadgroup.yaml
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
  name: forum
  namespace: forum-services
spec:
  metadata:
    annotations: {}
    labels:
      app: forum # 서비스는 레이블을 사용해 이 그룹의 워크로드를 대상으로 삼을 수 있다
  template:
    serviceAccount: forum-sa # 워크로드가 이 워크로드 그룹에 등록하려면 forum-sa 인증 토큰을 보유하고 있어야 한다
    network: vm-network # 이스티오가 동일한 네트워크에 있는 워크로드 사이의 직접 접근을 설정할 수 있도록 한다
  probe: # 이 워크로드 그룹의 인스턴스에서 실행되는 istio-agent는 HTTP GET 요청을 8080 포트의 /api/healthz 경로로 보내 앱의 준비 상태를 확인한다
    periodSeconds: 5
    initialDelaySeconds: 1
    httpGet:
      port: 8080
      path: /api/healthz

이 설정을 통해 VM 그룹의 자동 등록 및 트래픽 관리가 가능해진다.

#네임스페이스와 서비스 어카운트를 만들고 WorkloadGroup 설정을 클러스터에 적용해보자.
cat istio-in-action/book-source-code-master/ch13/workloadgroup.yaml
kubectl create namespace forum-services
kubectl create serviceaccount forum-sa -n forum-services
kubectl apply -f istio-in-action/book-source-code-master/ch13/workloadgroup.yaml

#
kubectl get-all -n forum-services
kubectl get workloadgroup -n forum-services
NAME    AGE
forum   2m2s

이제 클러스터는 WorkloadGroup에 명세된 forum-sa 서비스 어카운트의 유효한 토큰을 제시할 수 있는 워크로드를 자동으로 등록하도록 설정된다.

 

가상머신의 사이드카 설정을 생성하려면 istioctl 명령어와 WorkloadGroup을 반드시 활용해야 한다.

  1. istioctl은 WorkloadGroup의 정보와 클러스터 쿼리 결과를 기반으로 호스트 파일, 루트 인증서, 서비스 어카운트 토큰, 메시 설정 파일을 자동 생성해야 한다.
  2. 생성된 hosts 파일은 이스트-웨스트 게이트웨이 IP를 통해 istiod 서비스 DNS를 반드시 매핑해야 한다.
  3. **루트 인증서(root-cert.pem)**는 istiod와의 mTLS 통신 시 인증서 검증에 필수적으로 사용해야 한다.
  4. **서비스 어카운트 토큰(istio-token)**은 WorkloadGroup 소속 인증을 위해 반드시 istio-agent에 제공해야 한다.
  5. mesh.yaml 파일은 네트워크 및 공통 속성 설정을 포함하며, WorkloadGroup 정의에 따라 자동 적용해야 한다.
# Generates all the required configuration files for workload instance on a VM or non-Kubernetes environment from a WorkloadGroup artifact.
istioctl x workload entry configure -h
istioctl x workload entry configure \
--name forum \ # forum-services 네임스페이스에 있는 forum WorkloadGroup 을 읽고 워크로드 설정을 생성한다
--namespace forum-services \ # 상동
--clusterID "west-cluster" \ # 반드시 이스티오 설치 시 지정한 클러스터 이름으로 설정해야 한다
--externalIP $VM_IP \ # 워크로드가 클러스터와 동일한 네트워크에 있지 않은 경우 workloadIP 파라미터가 필요하다. 디폴트 설정에 의하면, 정의하지 않은 경우 네트워크에서 할당한 사설 IP를 사용한다.
--autoregister \ # 워크로드를 자동으로 등록하도록 설정한다.
-o /tmp/my-workload-files/ # 설정 파일을 저장할 디렉터리 위치를 명령 실행 위치에 대한 상대 경로로 지정한다.

#
istioctl x workload entry configure -f istio-in-action/book-source-code-master/ch13/workloadgroup.yaml -o /tmp/my-workload-files/ --clusterID "west-cluster" --autoregister
cat /tmp/my-workload-files/hosts
192.168.10.10 istiod.istio-system.svc

chown ubuntu:ubuntu -R /tmp/my-workload-files/
tree /tmp/my-workload-files/
├── cluster.env
├── hosts
├── istio-token
├── mesh.yaml
└── root-cert.pem

이 설정 파일들을 VM에 배포하면 서비스 프록시가 istiod와 보안 연결을 수립하고, xDS 구성을 수신해 메시에 통합된다.

 

 

가상머신으로 생성된 설정 파일 전송 시 SSH를 통한 안전한 복사를 반드시 수행해야 하며, 운영 환경에서는 자동화된 프로비저닝 도구를 활용해 수동 작업을 제거해야 한다.

# 임시로, 자신의 로컬 PC에 위 파일들 복사 : 바로 vm_ip 에 복사해도 되지만, 현재 실습 환경 편리성으로 경유 복사.
APP_IP=43.202.64.8
mkdir my-workload-files
scp -i ~/.ssh/kp-gasida.pem ubuntu@$APP_IP:/tmp/my-workload-files/\* ./my-workload-files # macOS
scp -i ~/.ssh/kp-gasida.pem ubuntu@$APP_IP:/tmp/my-workload-files/*  ./my-workload-files # linux

ls -al
openssl x509 -in ./my-workload-files/root-cert.pem -noout -text # istio CA 인증서
jwt decode $(cat ./my-workload-files/istio-token) # 토큰
...
Token claims
------------
{
  "aud": [
    "istio-ca"
  ],
  "exp": 1748083668,
  "iat": 1748080068,
  "iss": "https://kubernetes.default.svc.cluster.local",
  "kubernetes.io": {
    "namespace": "forum-services",
    "serviceaccount": {
      "name": "forum-sa",
      "uid": "64ab40b3-3bad-49f1-a1c9-0464d72c18c1"
    }
  },
  "nbf": 1748080068,
  "sub": "system:serviceaccount:forum-services:forum-sa"
}

cat ./my-workload-files/mesh.yaml
defaultConfig:
  discoveryAddress: istiod.istio-system.svc:15012
  meshId: usmesh
  proxyMetadata:
    CANONICAL_REVISION: latest
    CANONICAL_SERVICE: forum
    ISTIO_META_AUTO_REGISTER_GROUP: forum
    ISTIO_META_CLUSTER_ID: west-cluster
    ISTIO_META_DNS_CAPTURE: "true"
    ISTIO_META_MESH_ID: usmesh
    ISTIO_META_NETWORK: vm-network
    ISTIO_META_WORKLOAD_NAME: forum
    ISTIO_METAJSON_LABELS: '{"app":"forum","service.istio.io/canonical-name":"forum","service.istio.io/canonical-revision":"latest"}'
    POD_NAMESPACE: forum-services
    SERVICE_ACCOUNT: forum-sa
    TRUST_DOMAIN: cluster.local
  readinessProbe:
    httpGet:
      path: /api/healthz
      port: 8080
    initialDelaySeconds: 1
    periodSeconds: 5
  tracing:
    zipkin:
      address: zipkin.istio-system:9411


# 로컬 PC의 파일들을 forum-vm로 복사
FORUM=54.180.240.239
scp -i ~/.ssh/kp-gasida.pem ./my-workload-files/cluster.env ubuntu@$FORUM:/tmp/
scp -i ~/.ssh/kp-gasida.pem ./my-workload-files/istio-token ubuntu@$FORUM:/tmp/
scp -i ~/.ssh/kp-gasida.pem ./my-workload-files/mesh.yaml ubuntu@$FORUM:/tmp/
scp -i ~/.ssh/kp-gasida.pem ./my-workload-files/root-cert.pem ubuntu@$FORUM:/tmp/


# forum-vm 에서 파일 확인
ls -l /tmp
-rwxr--r-- 1 ubuntu ubuntu  714 May 24 19:08 cluster.env
-rwxr--r-- 1 ubuntu ubuntu  844 May 24 19:08 istio-token
-rwxr--r-- 1 ubuntu ubuntu  792 May 24 19:08 mesh.yaml
-rwxr--r-- 1 ubuntu ubuntu 1094 May 24 19:08 root-cert.pem
...

 

13.3.3 가상머신에 istio-agent 설치 및 설정하기* Installing and configuring the istio-agent in the VM

#forum-vm 가상머신에 istio-agent 를 다운로드하고 설치
#
cat /etc/resolv.conf
nameserver 127.0.0.53
...

ss -tnlp
State      Recv-Q     Send-Q         Local Address:Port         Peer Address:Port    Process
LISTEN     0          4096           127.0.0.53%lo:53                0.0.0.0:*        users:(("systemd-resolve",pid=357,fd=14))

ss -unlp
State     Recv-Q    Send-Q           Local Address:Port         Peer Address:Port    Process
UNCONN    0         0                127.0.0.53%lo:53                0.0.0.0:*        users:(("systemd-resolve",pid=357,fd=13))

resolvectl status
...
resolv.conf mode: stub # stub 모드로 설정, 127.0.0.53(local stub resolver)을 가리키며, systemd-resolved가 이 요청을 처리함

Link 2 (ens5)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 192.168.0.2
...

#
iptables -t nat -L -n -v
iptables -t filter -L -n -v
iptables -t mangle -L -n -v
iptables -t raw -L -n -v


# 데비안 패키지 형식으로 다운로드 후 설치
curl -LO https://storage.googleapis.com/istio-release/releases/1.17.8/deb/istio-sidecar.deb
file istio-sidecar.deb
dpkg -i istio-sidecar.deb
which pilot-agent
pilot-agent version
which envoy
envoy --version

tree /etc/istio
/etc/istio
├── config
│   └── mesh
├── envoy
│   ├── cluster.env
│   ├── envoy_bootstrap_tmpl.json
│   └── sidecar.env
├── extensions
│   ├── metadata-exchange-filter.compiled.wasm
│   ├── metadata-exchange-filter.wasm
│   ├── stats-filter.compiled.wasm
│   └── stats-filter.wasm
└── proxy

tree /var/lib/istio/
/var/lib/istio/
├── config
│   └── mesh
├── envoy
│   ├── envoy_bootstrap_tmpl.json
│   └── sidecar.env
├── extensions
│   ├── metadata-exchange-filter.compiled.wasm
│   ├── metadata-exchange-filter.wasm
│   ├── stats-filter.compiled.wasm
│   └── stats-filter.wasm
└── proxy

# istio-agent 는 특정 위치에서 설정 파일을 읽으므로, 복사해둔 파일을 옮겨보자
mkdir -p /etc/certs
mkdir -p /var/run/secrets/tokens

cp /tmp/root-cert.pem /etc/certs/root-cert.pem
cp /tmp/istio-token /var/run/secrets/tokens/istio-token
cp /tmp/cluster.env /var/lib/istio/envoy/cluster.env
cp /tmp/mesh.yaml /etc/istio/config/mesh


# istiod.istio-system.svc 요청을 istiod 인스턴스로 프록시하는 east-weat 게이트웨이 IP 주소로 정적으로 해석하도록 한다.
cat /etc/hosts
echo "192.168.10.10 istiod.istio-system.svc" | sudo sh -c 'cat >> /etc/hosts'
cat /etc/hosts

# istio-agent가 호스트네임 해석을 방해하지 않도록 hosts 파일에 forum-vm 머신의 호스트네임을 하드코딩하자 : 설정되어 있음
echo "$(hostname --all-ip-addresses | cut -d ' ' -f 1) $(hostname)" | sudo sh -c 'cat >> /etc/hosts'
cat /etc/hosts

 

클러스터 내 호스트네임 해석을 위해 DNS 프록시 설정이 반드시 필요하며, 이스트-웨스트 게이트웨이를 가리키는 네트워크 로드 밸런서를 활용해 동적 DNS 관리를 구현해야 한다.

 

#에이전트가 읽고 쓸 디렉터리에 소유자 권한을 부여
cat /etc/passwd | tail -n 3
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
istio-proxy:x:998:999::/var/lib/istio:/bin/sh

tree /etc/istio
chown -R istio-proxy /var/lib/istio /etc/certs /etc/istio/proxy /etc/istio/config /var/run/secrets /etc/certs/root-cert.pem

#
systemctl status istio
systemctl start istio
systemctl enable istio
systemctl status istio
...
     CGroup: /system.slice/istio.service
             ├─32047 sudo -E -u istio-proxy -s /bin/bash -c "ulimit -n 1024; INSTANCE_IP=15.165.15.104 POD_NAME=testpc POD_NAMESPACE=forum-services >
             ├─32140 /usr/local/bin/pilot-agent proxy
             └─32148 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drain-strategy immediate --local-address-ip-version >

May 24 20:10:05 testpc istio-start.sh[32128]: -A ISTIO_OUTPUT -o lo -p tcp -m tcp ! --dport 53 -m owner ! --gid-owner 998 -j RETURN
May 24 20:10:05 testpc istio-start.sh[32128]: -A ISTIO_OUTPUT -m owner --gid-owner 998 -j RETURN
May 24 20:10:05 testpc istio-start.sh[32128]: -A ISTIO_OUTPUT -d 127.0.0.53/3cat 2 -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 15053
May 24 20:10:05 testpc istio-start.sh[32128]: -A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
May 24 20:10:05 testpc istio-start.sh[32128]: -A ISTIO_OUTPUT -j ISTIO_REDIRECT
May 24 20:10:05 testpc istio-start.sh[32128]: -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
May 24 20:10:05 testpc istio-start.sh[32128]: COMMIT
May 24 20:10:05 testpc istio-start.sh[32128]: # Completed on Sat May 24 20:10:05 2025
May 24 20:10:05 testpc sudo[32047]:     root : PWD=/ ; USER=istio-proxy ; COMMAND=/bin/bash -c '\\/bin\\/bash -c ulimit\\ -n\\ 1024\\;\\ INSTANCE_IP>
May 24 20:10:05 testpc sudo[32047]: pam_unix(sudo:session): session opened for user istio-proxy(uid=998) by (uid=0)
...
## 혹은
journalctl -u istio -f
...

which istio-start.sh
cat /usr/local/bin/istio-start.sh


#
tree /etc/certs/
├── cert-chain.pem
├── key.pem
└── root-cert.pem

#
ps aux |grep istio

#
iptables -t nat -L -n -v
iptables -t filter -L -n -v
iptables -t mangle -L -n -v
iptables -t raw -L -n -v

 

에이전트 로그 확인하기 CHECKING THE AGENT LOGS

이스티오 에이전트 로그는 /var/log/istio/istio.log(표준 출력)와 /var/log/istio/istio.err(표준 오류)에 반드시 기록되며, 표준 출력 로그를 확인해 istiod 연결 성공 여부를 검증해야 한다.

#
cat /var/log/istio/istio.log | grep xdsproxy
2025-05-25T01:09:42.094214Z	info	xdsproxy	Initializing with upstream address "istiod.istio-system.svc:15012" and cluster "west-cluster"
2025-05-25T01:09:42.227661Z	info	xdsproxy	connected to upstream XDS server: istiod.istio-system.svc:15012

# 만약 로그 파일이 생성되지 않았다면? 서비스 시작이 실패한 경우에 그럴 수 있다. 아래 처럼 systemd 로그를 확인하자
journalctl -u istio -f

 

워크로드가 메시에 등록됐는지 확인하기 VERIFYING THAT THE WORKLOAD REGISTERED TO THE MESH

#
kubectl get workloadentries -n forum-services
NAME                              AGE    ADDRESS
forum-192.168.10.200-vm-network   110m   192.168.10.200

kc get workloadentries -n forum-services -o yaml
...
  status:
    conditions:
    - lastProbeTime: "2025-05-25T02:35:47.436392452Z"
      lastTransitionTime: "2025-05-25T02:35:47.436392780Z"
      message: 'Get "http://192.168.10.200:8080/api/healthz": dial tcp 127.0.0.6:0->192.168.10.200:8080:
        connect: connection refused'
      status: "False"
      type: Healthy
...

istioctl proxy-status
NAME                                                    CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                     VERSION
forum-vm.forum-services                                 west-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-d6549b9fc-rpg7l     1.17.0
webapp-684c568c59-9wtbt.istioinaction                   west-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-d6549b9fc-rpg7l     1.17.8
...

가상머신의 WorkloadEntry가 정상 등록되어 고유 주소가 표시되는 것을 반드시 확인해야 하며, 이후 트래픽 라우팅 동작을 점검해야 한다.

 

13.3.4 클러스터 서비스로 트래픽 라우팅하기 Routing traffic to cluster services

트래픽이 클러스터 서비스로 라우팅되는지 확인하기 위해 가상머신에서 webapp 워크로드로 curl 요청을 보내보자

# 신규 터미널 : 패킷 모니터링 https://github.com/gcla/termshark/blob/master/docs/UserGuide.md
tcpdump -i any -w - udp port 53 | termshark
## CTRL+C 로 취소 후 수집된 패킷이 termshark 에서 확인 가능

# testpc : 도메인 해석은 잘 됨
dig +short webapp.istioinaction
10.10.200.48

curl -s webapp.istioinaction/api/catalog/items/1 | jq
watch curl -s webapp.istioinaction/api/catalog/items/1

# 신규 터미널 : 15443 연결하는 envoy 프로세스(데이터 플래인) 확인!
watch -d ss -tnp 
watch -d 'ss -tnp | grep envoy'
State     Recv-Q  Send-Q   Local Address:Port     Peer Address:Port  Process                                                   
ESTAB 0      0      192.168.10.200:41238 192.168.10.10:15443 users:(("envoy",pid=3203,fd=40))
ESTAB 0      0      192.168.10.200:41242 192.168.10.10:15443 users:(("envoy",pid=3203,fd=41))
...

# 신규 터미널 : 
watch -d iptables -t nat -L -n -v
watch -d iptables -t raw -L -n -v


# k3s-s
kubectl get svc,ep -n istioinaction webapp
NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/webapp   ClusterIP   10.10.200.48   <none>        80/TCP    148m

NAME               ENDPOINTS         AGE
endpoints/webapp   172.16.0.8:8080   148m

 

실제 forum은 다이렉트로 연결하는게 아니라, 경유하여 접근한다.

가상머신에서 클러스터 서비스로의 트래픽 라우팅 과정은 다음 4단계로 수행된다.

  1. DNS 쿼리 리다이렉트
    • 애플리케이션의 DNS 요청은 iptables 규칙에 의해 Istio DNS 프록시로 전달되어야 한다.
    • DNS 프록시는 메시 내 서비스(쿠버네티스 서비스/ServiceEntry) 정보를 기반으로 IP 해석을 반드시 완료해야 한다.
  2. 아웃바운드 트래픽 캡처
    • 해석된 IP로의 요청은 iptablesEnvoy 사이드카로 리다이렉트해야 한다.
    • 이 단계에서 mTLS 암호화, 트래픽 정책 적용이 이루어진다.
  3. 이스트-웨스트 게이트웨이 경유
    • 멀티 네트워크 환경에서는 Envoy가 트래픽을 east-west 게이트웨이로 라우팅해야 한다.
    • 게이트웨이는 클러스터 네트워크와 VM 네트워크 간 보안 터널링을 반드시 수행한다.
  4. 최종 서비스 연결
    • 게이트웨이는 트래픽을 webapp 서비스로 전달하며, webapp은 내부 catalog 서비스와 연동된다.

 

 

13.3.5 트래픽을 WorkloadEntry로 라우팅하기 Routing traffic to the WorkloadEntry

클러스터 내부에서 가상머신 워크로드로 트래픽을 라우팅할 때는 WorkloadEntry의 IP 주소를 직접 사용하지 않는다. 쿠버네티스 파드 IP를 직접 사용하지 않는 것과 동일한 원리로, 플랫폼 유연성과 인스턴스 교체 용이성을 위해 쿠버네티스 서비스를 반드시 활용해야 한다.

  1. 레이블 기반 선택:
    • 쿠버네티스 서비스레이블 셀렉터를 통해 WorkloadEntry를 대상으로 지정해야 한다.
    • 예를 들어 app: forum 레이블이 부여된 WorkloadEntry를 선택하는 서비스를 생성한다.
  2. 동적 라우팅:
    • 이스티오는 서비스가 선택한 WorkloadEntry의 IP를 동적으로 설정해 트래픽을 전달해야 한다.
    • 이를 통해 VM 인스턴스의 추가/제거 시 클라이언트 측 변경 없이 자동 조정이 가능하다.
  3. 운영 장점:
    • 인스턴스 교체 용이: VM 장애 시 새 WorkloadEntry가 자동 등록되어 서비스 연속성을 보장해야 한다.
    • 멀티 클라우드 지원: 레이블 기반 라우팅을 통해 다양한 환경(온프레미스, 클라우드)의 워크로드를 통합 관리할 수 있다.

예시로 forum 서비스를 생성해 WorkloadEntry를 백엔드로 지정하면, 클러스터 내 애플리케이션은 VM의 IP를 알 필요 없이 서비스 DNS로 트래픽을 전송한다. 이스티오가 내부적으로 WorkloadEntry의 실제 엔드포인트로 라우팅을 처리한다.

#
cat istio-in-action/book-source-code-master/services/forum/kubernetes/forum-svc.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: forum
  name: forum
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: forum

kubectl apply -f istio-in-action/book-source-code-master/services/forum/kubernetes/forum-svc.yaml -n forum-services

kubectl get svc,ep -n forum-services
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/forum   ClusterIP   10.10.200.72   <none>        80/TCP    20s

NAME              ENDPOINTS   AGE
endpoints/forum   <none>      20s

#
istioctl proxy-config route deploy/webapp.istioinaction
istioctl proxy-config route deploy/webapp.istioinaction --name 80 -o json
...
      "name": "forum.forum-services.svc.cluster.local:80",
      "domains": [
          "forum.forum-services.svc.cluster.local",
          "forum.forum-services",
          "forum.forum-services.svc",
          "10.10.200.72"
      ],
      "routes": [
          {
              "name": "default",
              "match": {
                  "prefix": "/"
              },
              "route": {
                  "cluster": "outbound|80||forum.forum-services.svc.cluster.local",
...

#
istioctl proxy-config cluster deploy/webapp.istioinaction
istioctl proxy-config cluster deploy/webapp.istioinaction --fqdn forum.forum-services.svc.cluster.local -o json
istioctl proxy-config cluster deploy/webapp.istioinaction --fqdn forum.forum-services.svc.cluster.local
SERVICE FQDN                               PORT     SUBSET     DIRECTION     TYPE     DESTINATION RULE
forum.forum-services.svc.cluster.local     80       -          outbound      EDS   

# 아직 없다!
istioctl proxy-config endpoint deploy/webapp.istioinaction
istioctl proxy-config endpoint deploy/webapp.istioinaction | grep forum

#
istioctl proxy-config listener deploy/istio-eastwestgateway.istio-system
istioctl proxy-config route deploy/istio-eastwestgateway.istio-system
istioctl proxy-config cluster deploy/istio-eastwestgateway.istio-system
istioctl proxy-config endpoint deploy/istio-eastwestgateway.istio-system
istioctl proxy-config endpoint deploy/istio-eastwestgateway.istio-system | grep forum


#forum-vm 의 envoy config 확인
# [testpc] istioctl 설치
export ISTIOV=1.17.8
echo 'export ISTIOV=1.17.8' >> /root/.bashrc

curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl

#
curl -s localhost:15000/config_dump | istioctl proxy-config listener --file -
curl -s localhost:15000/config_dump | istioctl proxy-config route --file -
curl -s localhost:15000/config_dump | istioctl proxy-config clusters --file -
curl -s localhost:15000/config_dump | istioctl proxy-config endpoint --file -
curl -s localhost:15000/config_dump | istioctl proxy-config secret --file -
RESOURCE NAME     TYPE           STATUS     VALID CERT     SERIAL NUMBER                               NOT AFTER                NOT BEFORE
default           Cert Chain     ACTIVE     true           310309461583688467984066399721764000962     2025-05-26T01:09:42Z     2025-05-25T01:07:42Z
ROOTCA            CA             ACTIVE     true           46141372426695670978289547947687101983      2035-05-23T01:04:09Z     2025-05-25T01:04:09Z

서비스가 만들어지면 WorkloadEntry 엔드포인트가 선택되고, 이를 사용해 istiod가 데이터 플레인을 설정한다.

 

#
istioctl proxy-config cluster deploy/webapp.istioinaction --fqdn forum.forum-services.svc.cluster.local
istioctl proxy-config endpoint deploy/webapp.istioinaction | grep forum

# 로그 모니터링
kubectl logs -n istioinaction deploy/webapp -c istio-proxy -f
[2025-05-25T04:53:18.841Z] "GET /api/users HTTP/1.1" 503 UH no_healthy_upstream - "-" 0 19 0 - "" "beegoServer" "63377970-9d0f-4591-a4d4-039b4321863d" "forum.forum-services:80" "-" outbound|80||forum.forum-services.svc.cluster.local - 10.10.200.72:80 :0 - default
[2025-05-25T04:53:18.839Z] "GET /api/users HTTP/1.1" 500 - via_upstream - "-" 0 27 2 2 "" "curl/8.7.1" "63377970-9d0f-4591-a4d4-039b4321863d" "webapp.istioinaction.io" "172.16.0.8:8080" inbound|8080|| 127.0.0.6:36439 172.16.0.8:8080 :0 outbound_.80_._.webapp.istioinaction.svc.cluster.local default

# 자신의 PC
curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/catalog/
while true; do curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/catalog/ ; echo; date; sleep 1; done

curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/users
curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/users -I
HTTP/1.1 500 Internal Server Error

UH 응답 플래그는 정상 엔드포인트가 없어 트래픽을 라우팅할 수 없음을 의미하며, webapp에는 forum 서비스의 엔드포인트가 존재하지 않음을 나타낸다.

 

가상머신에서 forum 애플리케이션 시작하기 STARTING THE FORUM APPLICATION IN THE VM

# testpc
wget -O forum https://git.io/J3QrT
file forum
chmod +x forum
./forum

# testpc
curl http://localhost:8080/api/healthz -v

ss -tnlp | grep 8080
LISTEN 0      4096               *:8080             *:*    users:(("forum",pid=15081,fd=3))

# k8s-s
istioctl proxy-config cluster deploy/webapp.istioinaction --fqdn forum.forum-services.svc.cluster.local
istioctl proxy-config endpoint deploy/webapp.istioinaction --cluster 'outbound|80||forum.forum-services.svc.cluster.local' -o json
istioctl proxy-config endpoint deploy/webapp.istioinaction --cluster 'outbound|80||forum.forum-services.svc.cluster.local'
ENDPOINT                STATUS      OUTLIER CHECK     CLUSTER
192.168.10.200:8080     HEALTHY     OK                outbound|80||forum.forum-services.svc.cluster.local

#
kc get workloadentries -n forum-services -o yaml
...
  status:
    conditions:
    - lastProbeTime: "2025-05-25T05:02:23.116408430Z"
      lastTransitionTime: "2025-05-25T05:02:23.116409282Z"
      status: "True"
      type: Healthy
...

 

# 자신의 PC
curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/users
while true; do curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/users ; echo; date; sleep 1; done

# 로그 모니터링
kubectl logs -n istioinaction deploy/webapp -c istio-proxy -f
[2025-05-25T05:05:51.328Z] "GET /api/users HTTP/1.1" 200 - via_upstream - "-" 0 5645 28 27 "218.153.65.54" "beegoServer" "888f982d-f7f3-4232-ac0b-826cf65ef294" "forum.forum-services:80" "192.168.10.200:8080" outbound|80||forum.forum-services.svc.cluster.local 172.16.0.8:38170 10.10.200.72:80 218.153.65.54:0 - default
[2025-05-25T05:05:51.326Z] "GET /api/users HTTP/1.1" 200 - via_upstream - "-" 0 3679 30 30 "218.153.65.54" "curl/8.7.1" "888f982d-f7f3-4232-ac0b-826cf65ef294" "webapp.istioinaction.io" "172.16.0.8:8080" inbound|8080|| 127.0.0.6:36439 172.16.0.8:8080 218.153.65.54:0 outbound_.80_._.webapp.istioinaction.svc.cluster.local default

이스티오는 비정상 워크로드를 데이터 플레인에서 제외시켜 클라이언트 트래픽이 오류 발생 인스턴스로 전달되지 않도록 방지하며, 운영 환경에서 안정적인 라우팅을 제공한다.

 

13.3.6 컨트롤 플레인이 가상머신 설정: 상호 인증 강제 VMs are configured by the control plane: Enforcing mutual authentication

가상머신의 8080 포트가 외부에 노출되어 권한 없는 접근이 가능한 상황에서 PeerAuthentication을 통해 STRICT mTLS를 반드시 적용해야 메시 내 트래픽의 상호 인증을 강제할 수 있다.

#
cat istio-in-action/book-source-code-master/ch13/strict-peer-auth.yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT

kubectl apply -f istio-in-action/book-source-code-master/ch13/strict-peer-auth.yaml
kubectl get peerauthentication -A
# 자신의 PC에서 요청
curl -is $FORUM:8080/api/users -v

# istio-ingressgateway 경유 요청 
while true; do curl -s -H "Host: webapp.istioinaction.io" http://$APP_IP:30000/api/users ; echo; date; sleep 1; done

가상머신이 컨트롤 플레인의 설정을 준수함을 보여주며, PeerAuthentication뿐만 아니라 모든 이스티오 API로 프록시 정책을 자유롭게 적용할 수 있음을 의미한다.

 

+ Recent posts