지금까지는 이스티오 서비스 메시를 컨테이너 및 쿠버네티스 관점에서 다뤘다. 그러나 실제 워크로드는 자주 가상머신이나 물리 머신에서 실행된다. 엔터프라이즈는 규제 준수나 전문 지식 부족 등 다양한 이유로 워크로드를 온프레미스에서 실행해야 할 수 있다. 이번 장에서는 사이드카 프록시를 설치하고 설정함으로써 어떤 워크로드든 메시의 일부로 삼을 수 있는 방법을 보여준다. 이 접근법은 레거시 워크로드를 복원력 있고 안전하며 고가용성적인 방식으로 메시로 통합하길 원하는 엔터프라이즈에게 흥미로운 기능을 제공한다.
실습환경 구성
구성 : 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 통합이 완료되면 운영 효율성이 크게 개선될 전망이다.
가상머신의 고가용성 달성을 위해 WorkloadGroup과 WorkloadEntry 리소스를 반드시 활용해야 한다.
- WorkloadGroup은 쿠버네티스의 디플로이먼트와 유사하게 포트 설정, 레이블, 서비스 어카운트, 상태 프로브 방법 등 공통 속성을 정의한다.
- WorkloadEntry는 개별 가상머신 인스턴스를 나타내며, 인스턴스 상태와 주소 같은 고유 속성을 관리한다. 쿠버네티스의 파드와 동일한 역할을 수행한다.
- 수동으로 WorkloadEntry를 생성할 수 있지만, **워크로드 자동 등록(auto-registration)**을 통해 신규 VM이 메시에 자동 통합되도록 구성하는 것이 권장된다. 이를 통해 인스턴스 확장/축소 시 가용성을 유지할 수 있다.
이스티오는 쿠버네티스의 고가용성 메커니즘을 차용해 VM 워크로드의 운영 효율성을 높인다.

워크로드 자동 등록을 구현하려면 WorkloadGroup을 사전에 정의해야 하며, 가상머신이 ID 토큰으로 인증해 WorkloadEntry를 자동 생성하는 과정이 필수적이다.
- 트래픽 라우팅
쿠버네티스 서비스나 ServiceEntry가 레이블 셀렉터를 통해 WorkloadEntry를 백엔드로 선택해야 한다. 이를 통해 VM과 파드를 동일한 서비스 엔드포인트로 통합 관리할 수 있다. - 마이그레이션 전략
레거시 VM과 현대화 파드를 병렬 운영하면서 트래픽 전환 기능(예: 카나리아 배포)을 적용해야 한다. 오류 발생 시 VM으로 트래픽을 즉시 되돌리는 롤백 메커니즘이 반드시 필요하다. - 운영 효율성
워크로드 상태에 따라 자동으로 인스턴스를 확장/축소할 수 있어 고가용성을 보장한다. 클라이언트 측 변경 없이 인프라 변경이 가능하다는 장점이 있다.
서비스 메시에서 readiness 프로브는 반드시 이스티오가 관리하여 워크로드의 트래픽 처리 준비 상태를 확인해야 한다. liveness 프로브는 플랫폼(예: 쿠버네티스, AWS/Azure/GCP)의 책임이며, 애플리케이션 건강 상태를 모니터링하고 장애 시 인스턴스 자동 복구를 수행해야 한다.
- Readiness 프로브: 이스티오가 트래픽 수신 가능 여부를 검사해 메시 내 통합을 제어한다.
- Liveness 프로브: 클라우드 플랫폼별 자동 복구 기능(예: AWS Auto Scaling, Azure VM 복구)을 반드시 활용해야 한다.
따라서 가상머신 운영 시 플랫폼의 헬스 체크 및 복구 메커니즘을 필수적으로 구성해야 한다.

이스티오의 가상머신 Readiness 프로브 구현 방식
가상머신의 readiness 프로브는 WorkloadGroup에 정의된 설정에 따라 istio-agent가 주기적으로 애플리케이션 상태를 검사해야 한다. 검사 결과는 istiod에 보고되며, 정상 상태일 때만 트래픽을 수신하도록 데이터 플레인을 구성한다.
핵심 메커니즘
- 상태 보고 및 라우팅 제어
- istio-agent는 애플리케이션 헬스 체크 결과를 istiod에 전달해야 한다.
- 비정상 상태 감지 시 해당 VM 엔드포인트를 데이터 플레인에서 즉시 제거해야 한다.
- 프로브 구성 권장 사항
- Readiness 프로브:
- 공격적 설정 적용이 필수적이다(예: 5초 주기 체크, 2회 연속 실패 시 제외).
- 오류 발생 시 트래픽 유입을 즉시 차단해야 한다.
- Liveness 프로브:
- 클라우드 플랫폼(AWS/Azure/GCP)의 자동 복구 기능을 반드시 활용해야 한다.
- 보수적 설정 적용(예: 30초 주기 체크, 3회 연속 실패 후 인스턴스 재생성).
운영 주의사항
- 유예 기간 고려:
인스턴스 종료 전 진행 중인 요청 완료를 위해 grace period를 설정해야 한다. - 실패 순서 관리:
Readiness 프로브가 Liveness 프로브보다 먼저 실패하도록 설계해 불필요한 인스턴스 재생성을 방지해야 한다.
이스티오는 쿠버네티스의 헬스 체크 메커니즘을 확장해 VM 환경에 적용하며, 운영자 반드시 인프라 계층과의 연동을 검증해야 한다.

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

- DNS 쿼리 처리 메커니즘
- 애플리케이션의 DNS 요청은 iptables 규칙을 통해 DNS 프록시로 전달해야 한다.
- DNS 프록시는 istiod가 제공하는 메시 서비스 정보(쿠버네티스 서비스, ServiceEntry)로 동적으로 업데이트되어야 한다.
- NDS(Name Discovery Service) API
- 서비스 추가/변경 시 NDS API를 통해 DNS 프록시에 실시간으로 변경 사항을 반영해야 한다.
- 이를 통해 외부 DNS 서버(external-dns) 의존성을 제거하고 통합 관리가 가능하다.
- 장점
- 멀티 네트워크 환경에서도 DNS 해석이 자동화되어 VM-파드 간 원활한 통신을 보장해야 한다.
- DNS 프록시는 TCP/UDP 트래픽 캡처 기능과 결합되어 애플리케이션 수정 없이 트래픽 제어가 가능하다.
이스티오 1.8 이상에서는 DNS 프록시가 기본 구성 요소로 포함되며, 운영자는 VM의 /etc/resolv.conf 설정을 DNS 프록시(127.0.0.1:15053)로 변경해야 한다.
Istio DNS Proxying은 DNS 라운드트립을 반드시 제거해야 응답 속도 개선과 CoreDNS 부하 감소를 동시에 달성할 수 있다.
- 트래픽 처리 방식
- 메시 내 Kubernetes 서비스, Pod, ServiceEntry 정보를 미리 동기화해 애플리케이션 변경 없이 DNS 해석을 수행해야 한다.
- 외부 서비스(예: AWS RDS)는 ServiceEntry 등록을 통해 VIP(Class E 대역)를 자동 할당해야 하며, 이를 통해 포트 충돌 없이 리스너를 구성해야 한다.
- 운영 효율성
- 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
},
...
- 네트워크 분리 시:
- VM ↔ istiod/서비스 직접 통신 불가 → 게이트웨이를 통한 트래픽 프록시 필수
- 포트 노출:
- 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)은 가상머신 집합의 공통 속성을 반드시 정의해야 한다.
- 레이블(labels): 쿠버네티스 서비스가 워크로드 엔트리를 선택하기 위한 기준으로 활용해야 한다.
- 네트워크(network): 동일 네트워크 시 IP 직접 접근, 다른 네트워크 시 이스트-웨스트 게이트웨이를 통해 트래픽을 라우팅해야 한다.
- 서비스 어카운트(serviceAccount): 워크로드 식별을 위해 반드시 지정해야 하며, VM은 해당 토큰으로 인증을 완료해야 한다.
- 헬스 체크(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을 반드시 활용해야 한다.
- istioctl은 WorkloadGroup의 정보와 클러스터 쿼리 결과를 기반으로 호스트 파일, 루트 인증서, 서비스 어카운트 토큰, 메시 설정 파일을 자동 생성해야 한다.
- 생성된 hosts 파일은 이스트-웨스트 게이트웨이 IP를 통해 istiod 서비스 DNS를 반드시 매핑해야 한다.
- **루트 인증서(root-cert.pem)**는 istiod와의 mTLS 통신 시 인증서 검증에 필수적으로 사용해야 한다.
- **서비스 어카운트 토큰(istio-token)**은 WorkloadGroup 소속 인증을 위해 반드시 istio-agent에 제공해야 한다.
- 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



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

- DNS 쿼리 리다이렉트
- 애플리케이션의 DNS 요청은 iptables 규칙에 의해 Istio DNS 프록시로 전달되어야 한다.
- DNS 프록시는 메시 내 서비스(쿠버네티스 서비스/ServiceEntry) 정보를 기반으로 IP 해석을 반드시 완료해야 한다.
- 아웃바운드 트래픽 캡처
- 해석된 IP로의 요청은 iptables가 Envoy 사이드카로 리다이렉트해야 한다.
- 이 단계에서 mTLS 암호화, 트래픽 정책 적용이 이루어진다.
- 이스트-웨스트 게이트웨이 경유
- 멀티 네트워크 환경에서는 Envoy가 트래픽을 east-west 게이트웨이로 라우팅해야 한다.
- 게이트웨이는 클러스터 네트워크와 VM 네트워크 간 보안 터널링을 반드시 수행한다.
- 최종 서비스 연결
- 게이트웨이는 트래픽을 webapp 서비스로 전달하며, webapp은 내부 catalog 서비스와 연동된다.
13.3.5 트래픽을 WorkloadEntry로 라우팅하기 Routing traffic to the WorkloadEntry
클러스터 내부에서 가상머신 워크로드로 트래픽을 라우팅할 때는 WorkloadEntry의 IP 주소를 직접 사용하지 않는다. 쿠버네티스 파드 IP를 직접 사용하지 않는 것과 동일한 원리로, 플랫폼 유연성과 인스턴스 교체 용이성을 위해 쿠버네티스 서비스를 반드시 활용해야 한다.
- 레이블 기반 선택:
- 쿠버네티스 서비스는 레이블 셀렉터를 통해 WorkloadEntry를 대상으로 지정해야 한다.
- 예를 들어 app: forum 레이블이 부여된 WorkloadEntry를 선택하는 서비스를 생성한다.
- 동적 라우팅:
- 이스티오는 서비스가 선택한 WorkloadEntry의 IP를 동적으로 설정해 트래픽을 전달해야 한다.
- 이를 통해 VM 인스턴스의 추가/제거 시 클라이언트 측 변경 없이 자동 조정이 가능하다.
- 운영 장점:
- 인스턴스 교체 용이: 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로 프록시 정책을 자유롭게 적용할 수 있음을 의미한다.