들어가며
이번 포스팅은 CloudNet@ 커뮤니티에서 주최하는 KANS 스터디 7주 차 주제인 "Service Mesh: Istio-Mode(Sidecar, Ambient)"에 대해서 정리한 내용입니다.
실습환경 구성
이번 주차 실습은 VPC 1개(퍼블릭 서브넷 2개), Kubernetes 클러스터 구성용 EC2 인스턴스 3대 (Ubuntu 22.04 LTS, t3.xlarge - vCPU 4 , Mem 16) , 외부 PC 통신 및 envoy 실습용 testpc 1대 (t3.small)로 구성됩니다.
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-7w.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 kans-7w.yaml --stack-name mylab --parameter-overrides KeyName=devlos SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --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
# 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)
...
생성된 인스턴스를 살펴보면 다음과 같이 잘 배포된 것을 확인할 수 있습니다.
testpc에 접속하여 네트워크 인터페이스와 IP를 확인합니다.
Envoy 란?
Envoy 프로젝트는 CNCF 재단에서 쿠버네티스와 프로메테우스 다음으로 3번째로 졸업한 프로젝트로써 , 대규모 현대 서비스 지향 아키텍처를 위해 설계된 L7 프록시 및 통신 버스입니다. Envooy는 네트워크는 애플리케이션에 투명하게 하여 문제가 발생하면 문제의 근원을 쉽게 파악할 수 있어야 합니다.
아래는 Envoy의 high level architecture를 보여줍니다.
출처 - https://www.envoyproxy.io/docs/envoy/latest/intro/life_of_a_request
프론트에서 envoy로 통신이 들어오는 부분을 downstream이라 하고, envoy에서 backend로 통신이 나가는 부분을 upstream이라고 합니다.
architecture에 명시된 각 컴포넌트들의 역할은 다음과 같습니다.
- Cluster : envoy 가 트래픽을 포워드 할 수 있는 논리적인 서비스 (엔드포인트 세트), 실제 요청이 처리되는 IP 또는 엔드포인트의 묶음을 의미.
- Endpoint : IP 주소, 네트워크 노드로 클러스터로 그룹핑됨, 실제 접근이 가능한 엔드포인트를 의미. 엔드포인트가 모여서 하나의 Cluster 가 된다.
- Listener : 무엇을 받을지 그리고 어떻게 처리할지 IP/Port를 바인딩하고, 요청 처리 측면에서 다운스트림을 조정하는 역할.
- Route : Listener로 들어온 요청을 어디로 라우팅 할 것인지를 정의. 라우팅 대상은 일반적으로 Cluster라는 것에 대해 이뤄지게 된다.
- Filter : Listener로부터 서비스에 트래픽을 전달하기까지 요청 처리 파이프라인
- UpStream : envoy 요청을 포워딩해서 연결하는 백엔드 네트워크 노드 - 사이드카일 때 application app, 아닐 때 원격 백엔드
- DownStream : 사이드카 모델이 아니고, container에 연결을 원하는 것인 경우 원격 클라이언트입니다
Envoy는 이벤트 기반 스레드 모델을 사용합니다. 메인 스레드는 서버 수명 주기, 구성 처리, 통계 등을 담당하고 일부 작업자 스레드는 요청을 처리합니다.
모든 스레드는 이벤트 루프( libevent )를 중심으로 작동하며 주어진 다운스트림 TCP 연결(모든 멀티플렉싱 된 스트림 포함)은 수명 동안 정확히 하나의 작업자 스레드에 의해 처리됩니다. 각 작업자 스레드는 업스트림 엔드포인트에 대한 자체 TCP 연결 풀을 유지 관리합니다.
UDP 처리에서는 SO_REUSEPORT를 사용하여 커널이 소스/대상 IP:port 튜플을 동일한 작업자 스레드에 일관되게 해시하도록 합니다. UDP 필터 상태는 주어진 작업자 스레드에 대해 공유되며, 필터는 필요에 따라 세션 의미 체계를 제공해야 합니다.
Envoy는 네트워크 구성을 동적으로 관리하기 위해 강력한 API를 제공하는 것이 중요합니다. 위에서 설명한 내용을 토대로 Envoy 구성에 대하여 살펴보면 다음과 같은 API가 있는 것을 확인할 수 있습니다.
- LDS - Listener Discovery Service
- RDS - Route Discovery Service
- CDS - Cluseter Discovery Service
- EDS - Endpoint Discovery Service
각각의 xDS들이 GRPC를 통해 Envoy 컨테이너와 통신합니다.
xDS API들에 대해서는 해당 그림이 더 잘 설명되어 있습니다.
실습에서 계속 xDS에 대한 언급이 있으므로 주요 구성요소를 조금 더 살펴보겠습니다,
- Configuration (좌측):
- 여러 소스에서 수집된 설정 데이터입니다. 이는 다양한 네트워크, 보안, 라우팅, 엔드포인트 정보를 포함할 수 있습니다. 컨트롤 플레인은 이러한 설정 데이터를 모아서 Envoy에 전달합니다.
- Control Plane (중앙):
- Envoy가 사용할 설정 정보를 수집하고 관리하는 역할을 합니다. 컨트롤 플레인은 xDS API를 통해 Envoy에게 다양한 설정 데이터를 제공합니다. 여기서 각 API는 서로 다른 유형의 설정을 제공하며, 이를 통해 Envoy가 트래픽을 효과적으로 제어할 수 있습니다.
- xDS APIs (우측):
- 컨트롤 플레인은 다양한 xDS API를 통해 Envoy로 데이터를 전송하며, 각 API의 역할은 다음과 같습니다
- SDS (Secret Discovery Service): Envoy가 TLS 인증서와 같은 보안 정보를 동적으로 가져오는 서비스
- EDS (Endpoint Discovery Service): 백엔드 서비스의 엔드포인트 정보를 Envoy에게 제공
- CDS (Cluster Discovery Service): 클러스터 정보를 전달합니다. 클러스터는 백엔드 서버의 그룹으로, Envoy는 이를 통해 어떤 서버로 트래픽을 보낼지 결정
- SRDS (Scoped Route Discovery Service): 특정 범위 내에서 라우팅 규칙을 제공하여, Envoy가 요청을 적절한 경로로 전달
- LDS (Listener Discovery Service): Envoy의 리스너 설정을 관리합니다. 리스너는 네트워크 요청을 수신하는 엔티티
- ECDS (Extension Configuration Discovery Service): Envoy의 동작을 확장하는 데 사용되는 추가적인 설정 데이터를 제공
- RDS (Route Discovery Service): 트래픽 라우팅 규칙을 제공합니다. Envoy는 이를 통해 클라이언트 요청을 적절한 백엔드 서비스로 라우팅
- VHDS (Virtual Host Discovery Service): 가상 호스트에 대한 라우팅 정보를 제공
- RTDS (Runtime Discovery Service): 런타임 중에 변경 가능한 설정 값을 동적으로 제공하는 서비스
- GRPC (Envoy컨테이너와의 통신):
- 앞서 설명했듯이 컨트롤 플레인은 Envoy와 gRPC를 통해 통신합니다. gRPC는 고성능 원격 프로시저 호출(Remote Procedure Call) 시스템으로, Envoy는 이 프로토콜을 통해 컨트롤 플레인으로부터 동적으로 설정을 받아 트래픽을 제어
실습 - Envoy 사용
# 설치
wget -O- https://apt.envoyproxy.io/signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/envoy-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/envoy-keyring.gpg] https://apt.envoyproxy.io focal main" | sudo tee /etc/apt/sources.list.d/envoy.list
sudo apt-get update && sudo apt-get install envoy -y
# 확인
envoy --version
# 도움말
envoy --help
Envoy에 config를 주입시킵니다. listener에 어떤 IP던지 port 10000번으로 받고 filter chain을 통해 service_envoyproxy_io 클러스터로 라우팅 하고, endpoint www.envoyproxy.io로 host rewrite 하도록 되어 있습니다.
# (터미널1) 데모 config 적용하여 실행
curl -O https://www.envoyproxy.io/docs/envoy/latest/_downloads/92dcb9714fb6bc288d042029b34c0de4/envoy-demo.yaml
envoy -c envoy-demo.yaml
# (터미널2) 정보 확인
ss -tnlp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 0.0.0.0:10000 0.0.0.0:* users:(("envoy",pid=8007,fd=18),("envoy",pid=8007,fd=16))
# 접속 테스트
curl -s http://127.0.0.1:10000 | grep -o "<title>.*</title>"
# 외부 접속 정보 출력
echo -e "http://$(curl -s ipinfo.io/ip):10000"
--------------------
# 자신의 PC 웹브라우저에서 외부 접속 정보 접속 확인!
# k3s-s 에서 접속 테스트
curl -s http://xxx.xxx.xxx.xxx:10000 | grep -o "<title>.*</title>"
--------------------
# 연결 정보 확인
ss -tnp
# (터미널1) envoy 실행 취소(CTRL+C) 후 (관리자페이지) 설정 덮어쓰기 - 링크
cat <<EOT> envoy-override.yaml
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9902
EOT
envoy -c envoy-demo.yaml --config-yaml "$(cat envoy-override.yaml)"
# envoy 관리페이지 외부 접속 정보 출력
echo -e "http://$(curl -s ipinfo.io/ip):9902"
http://54.180.243.135:9902
설치 후 서비스에 접속하면 다음과 같이 사설 IP가 istio proxy 공식홈페이지로 리다이렉트 되는 로그를 확인할 수 있습니다.
또한 다음과 같이 관리자 페이지에 접속하여 여러 가지 정보들을 확인할 수 있습니다.
프로메테우스 포맷도 학인할 수 있습니다.
이러한 방법으로 여러 정보를 확인할 수 있도록 환경을 제공해서 envoy를 많이 사용합니다.
Istio 소개
Istio를 이해하려면 Service Mesh가 무엇인지 알아야 합니다. Service Mesh는 MSA환경의 시스템 전체 모니터링의 어렵고, 운영 시 시스템 장애나 문제 발생 원인, 트래픽 병목구간을 파악하기 힘듭니다.
이러한 불편한 점을 위에서 언급한 Envoy와 같은 프록시를 통해 서비스 간 통신을 시키고, 이를 모니터링하거나 컨트롤하는 것을 Service Mesh 기술이라고 합니다.
그래서 회사에서도 이러한 목적으로 인해 Service Mesh 기술도입을 검토하게 되었습니다. - 이전글
Istio는 이러한 Service Mesh 기술의 구현체라 할 수 있습니다.
Istio는 기존 분산 애플리케이션에 투명하게 레이어링 되는 오픈 소스 서비스 메시입니다. Istio의 강력한 기능은 서비스를 보안, 연결 및 모니터링하는 균일하고 효율적인 방법을 제공합니다. Istio는 서비스 코드를 거의 변경하지 않고도 부하 분산, 서비스 간 인증 및 모니터링을 제공합니다.
Istio가 제공하는 기능을 요약하자면 다음과 같습니다.
- 상호 TLS 암호화, 강력한 ID 기반 인증 및 권한 부여를 통해 클러스터에서 안전한 서비스 간 통신
- HTTP, gRPC, WebSocket 및 TCP 트래픽에 대한 자동 로드 밸런싱
- 풍부한 라우팅 규칙, 재시도, 장애 조치 및 오류 주입을 통한 트래픽 동작의 세부적인 제어
- 액세스 제어, 속도 제한 및 할당량을 지원하는 플러그형 정책 계층 및 구성 API
- 클러스터 유입 및 유출을 포함하여 클러스터 내의 모든 트래픽에 대한 자동 메트릭, 로그 및 추적
이스티오는 크게 Sideca 모드와 Ambient 모드로 사용할 수 있습니다.
(Ambient 모드의 존재는 스터디를 통해 처음 알게 되었네요)
두 모드는 서비스 간 통신을 관리하는 방식에서 차이가 있으며, 각각의 장단점이 있습니다.
출처 - https://www.solo.io/blog/istio-more-for-less/
사이드카 모드는 전통적으로 Istio에서 가장 많이 사용되는 방식입니다. 각 애플리케이션 컨테이너(Pod)마다 Envoy 프락시가 함께 배포되며, 이 프록시는 애플리케이션의 모든 네트워크 트래픽을 제어합니다.
엠비언트 모드는 Istio의 새로운 아키텍처로, 사이드카를 제거하고 대신 노드 레벨에서 서비스 메쉬 기능을 구현하는 방식입니다. 이 모드는 사이드카 없는 서비스 메쉬를 제공하여 리소스 사용량을 줄이고, 운영을 간소화하려는 목적으로 설계되었습니다.
특징 | 사이드카 모드 | 엠비언트 모드 |
프록시 배포 위치 | 각 서비스마다 사이드카 프록시 배포 | 노드 레벨의 공유 프록시 사용 |
리소스 사용량 | 높은 리소스 오버헤드 | 리소스 사용량 감소 |
운영 복잡성 | 사이드카 배포 및 관리 복잡 | 중앙화된 프록시로 운영 간소화 |
트래픽 제어 | 서비스별 세밀한 트래픽 제어 가능 | 세밀한 제어가 어려움 |
보안 및 인증 | 각 프록시에서 보안(암호화, 인증) 처리 | 노드 레벨에서 처리 |
성능 및 확장성 | 성능은 좋으나 리소스 소모가 많음 | 리소스 효율적이나, 트래픽 처리 성능은 확인 필요 |
스터디에서는 이 중에서 가장 잘 알려진 모드인 사이드카 모드를 기준으로 진행하였습니다.
공식 홈페이지에서는 이스티오 아키텍처를 다음과 같이 표현했습니다.
출처 - https://istio.io/latest/docs/ops/deployment/architecture/
Istio Mesh는 Control Plane과 Data Plane으로 나눌 수 있고, 세부 Component에 대한 설명은 다음과 같습니다.
컨트롤 플레인(Control Plane)
- Istiod는 컨트롤 플레인의 핵심 구성 요소로, 서비스 메쉬의 전반적인 관리를 담당합니다.
- 주요 컴포넌트:
- 파일럿(Pilot): 모든 Envoy 사이드카에서 프록시 라우팅 규칙을 관리하며, 서비스 디스커버리와 로드 밸런싱 설정을 제공합니다.
- 갤리(Galley): Istio와 쿠버네티스(TLS 연결 및 파일럿에 필요한 설정)를 연결해 주는 역할을 합니다. 서비스 메시 구성 데이터를 검증하고 변환합니다.
- 시타델(Citadel): 보안 기능을 담당하며, TLS 인증서 발급 및 관리를 통해 서비스 간 통신의 암호화를 수행합니다.
- 컨트롤 플레인의 역할:
- 새 서비스 존재 파악
- 텔레메트리 지시
- 라우팅, 로드밸런싱 설정
- 보안 관련 정보 처리
- 전체 데이터 관리
데이터 플레인(Data Plane)
- 서비스 A와 서비스 B는 각각 프록시(Envoy 사이드카)를 통해 서로 통신하고 있습니다.
- 데이터 플레인에는 각 서비스에 부착된 Envoy 사이드카 프록시가 있고, 이 프록시는 서비스에서 나가는 요청과 들어오는 요청을 가로채서 처리합니다.
- Envoy 사이드카는 다음과 같은 역할을 수행합니다:
- 들어오는 요청을 로깅하고, 나가는 요청의 발신 및 상태를 확인합니다.
- 서비스 상태 점검과 트래픽 관리가 이루어집니다.
- 데이터 플레인의 역할:
- 상태 확인
- 라우팅
- 서비스 디스커버리
- 로드 밸런싱
- 보안 (예: TLS 암호화)
- 텔레메트리 (원격 측정)
실습 - Istio 설치
이스티오 공식 도큐먼트에 설명이 잘 나와있기 때문에 해당 문서에 익숙해지는 것을 다들 추천합니다. - 공식문서
이스티오는 노드별로 zTunnel을 배치하여 동작하는 엠비언트모드와 Pod별로 envoy-proxy를 배치하는 Sidecar mode가 있습니다.
실습은 sidecar mode의 demo(istio-egressgateway와 ingressgateway가 설치되고, 환경설정이 기본으로 많이 제공됨) 진행하는데, 모든 pod에 istio sidecar가 부착됩니다.
# istioctl 설치
export ISTIOV=1.23.2
echo "export ISTIOV=1.23.2" >> /etc/profile
curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV TARGET_ARCH=x86_64 sh -
tree istio-$ISTIOV -L 2 # sample yaml 포함
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
istioctl version --remote=false
# (default 프로파일) 컨트롤 플레인 배포 - 링크 Customizing
# The istioctl command supports the full IstioOperator API via command-line options for individual settings or for passing a yaml file containing an IstioOperator custom resource (CR).
istioctl profile list
istioctl profile dump default
istioctl profile dump --config-path components.ingressGateways
istioctl profile dump --config-path values.gateways.istio-ingressgateway
istioctl profile dump demo
istioctl profile dump demo > demo-profile.yaml
vi demo-profile.yaml # 복잡성을 줄이게 실습 시나리오 환경 맞춤
--------------------
egressGateways:
- enabled: false
--------------------
istioctl install -f demo-profile.yaml -y
✔ Istio core installed ⛵️
✔ Istiod installed 🧠
✔ Ingress gateways installed 🛬
✔ Installation complete
# 설치 확인 : istiod, istio-ingressgateway
kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
kubectl get crd | grep istio.io | sort
# istio-ingressgateway 의 envoy 버전 확인
kubectl exec -it deploy/istio-ingressgateway -n istio-system -c istio-proxy -- envoy --version
envoy version: 6c72b2179f5a58988b920a55b0be8346de3f7b35/1.31.2-dev/Clean/RELEASE/BoringSSL
# istio-ingressgateway 서비스 NodePort로 변경
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"type":"NodePort"}}'
# istio-ingressgateway 서비스 확인
kubectl get svc,ep -n istio-system istio-ingressgateway
## istio-ingressgateway 서비스 포트 정보 확인
kubectl get svc -n istio-system istio-ingressgateway -o jsonpath={.spec.ports[*]} | jq
## istio-ingressgateway 디플로이먼트 파드의 포트 정보 확인
kubectl get deploy/istio-ingressgateway -n istio-system -o jsonpath={.spec.template.spec.containers[0].ports[*]} | jq
kubectl get deploy/istio-ingressgateway -n istio-system -o jsonpath={.spec.template.spec.containers[0].readinessProbe} | jq
# istiod(컨트롤플레인) 디플로이먼트 정보 확인
kubectl exec -it deployment.apps/istiod -n istio-system -- ss -tnlp
kubectl exec -it deployment.apps/istiod -n istio-system -- ss -tnp
kubectl exec -it deployment.apps/istiod -n istio-system -- ps -ef
UID PID PPID C STIME TTY TIME CMD
istio-p+ 1 0 0 05:27 ? 00:00:07 /usr/local/bin/pilot-discovery discovery --monitoringAddr=:15014 --log_output_l
# istio-ingressgateway 디플로이먼트 정보 확인
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -tnlp
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -tnp
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ps -ef
istio-p+ 1 0 0 05:27 ? 00:00:01 /usr/local/bin/pilot-agent proxy router --domain istio-system.svc.cluster.local
istio-p+ 15 1 0 05:27 ? 00:00:11 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drai
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- cat /etc/istio/proxy/envoy-rev.json
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -xnlp
kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -xnp
설치하면 다음과 같은 디렉터리가 생성됩니다.
프로파일을 확인했습니다.
실습의 복잡도를 낮추기 위하여 egress를 비활성화합니다.
서비스를 nodeport로 변경합니다.
포트 정보를 확인합니다.
내부 통신에서는 유닉스 소켓을 사용하는 것을 확인할 수 있습니다.
실습 - Istio sidecar auto injection 설정 및 접속 테스트
# mutating Webhook admisstion controller 사용
kubectl label namespace default istio-injection=enabled
kubectl get ns -L istio-injection
k3s-s 노드에 Istio 접속 테스트를 위한 변수를 지정합니다.
k3s-s)
# istio ingress gw NodePort(HTTP 접속용) 변수 지정 : 아래 ports[0] 은 어떤 용도의 포트일까요?
export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
echo $IGWHTTP
IGWHTTP=<각자 자신의 NodePort>
# /etc/hosts 파일 수정
MYDOMAIN=<각자 자신의 www 도메인> # 단, 사용하고 있지 않는 공인 도메인을 사용 할 것
MYDOMAIN=www.devlos.dev
echo "<istio-ingressgateway 파드가 있는 워커 노드> $MYDOMAIN" >> /etc/hosts
MYDOMAIN=<각자 자신의 www 도메인>
export MYDOMAIN=www.devlos.dev
echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile
# istio ingress gw 접속 테스트 : 아직은 설정이 없어서 접속 실패가 된다
curl -v -s $MYDOMAIN:$IGWHTTP
쿠버네티스 클러스터에서 접속 실패를 확인했습니다.
testpc 노드 및 개인 pc에서도 istio에 설정이 아무것도 되어 있지 않아서 실패합니다.
실습 - Istio 외부 노출
# 로그 모니터링
kubectl get pod -n istio-system -l app=istiod
kubetail -n istio-system -l app=istiod -f
kubetail -n istio-system -l app=istio-ingressgateway -f
다음으로 deployment와 서비스를 배포합니다.
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-websrv
spec:
replicas: 1
selector:
matchLabels:
app: deploy-websrv
template:
metadata:
labels:
app: deploy-websrv
spec:
terminationGracePeriodSeconds: 0
containers:
- name: deploy-websrv
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-clusterip
spec:
ports:
- name: svc-webport
port: 80
targetPort: 80
selector:
app: deploy-websrv
type: ClusterIP
EOF
디플로이먼트 배포 시 istiod의 로그를 살펴보면 sidecar injection을 수행하는 것을 알 수 있습니다.
# 사이드카 컨테이너 배포 확인
kubectl get pod,svc,ep -o wide
kc describe pod
실습 - Istio Gateway/VirtualService 설정
실습 - Istio를 통한 Nginx 파드 접속 테스트
istio-ingressgateway의 로그도 잘 출력됩니다.
istio-proxy에서 사용중인 포트 정보들을 확인해봤습니다.
포드들에 대한 세부 설명은 공식 홈페이지에 잘 나와있습니다 - 링크
# istio-proxy 사용자 정보 확인 : 1337:1337
kubectl exec -it deploy/deploy-websrv -c istio-proxy -- tail -n 3 /etc/passwd
# envoy 설정 정보 확인 : dynamic_resources , static_resources - listeners : 출력되는 IP가 누구인지 확인 해보자
kubectl exec -it deploy/deploy-websrv -c istio-proxy -- cat /etc/istio/proxy/envoy-rev.json
kubectl exec -it deploy/deploy-websrv -c istio-proxy -- ss -nlp
kubectl exec -it deploy/deploy-websrv -c istio-proxy -- ss -np
kubectl exec -it deploy/deploy-websrv -c istio-proxy -- netstat -np
kubectl get pod,svc -A -owide
kubectl exec -it deploy/deploy-websrv -c istio-proxy -- ps -ef
kubectl exec -it deploy/istiod -n istio-system -- ps -ef
kubectl exec -it deploy/istiod -n istio-system -- netstat -antp
kubectl exec -it deploy/istiod -n istio-system -- ss -nlp
kubectl exec -it deploy/istiod -n istio-system -- ss -np
파드 디버깅을 해보면 사이드카 컨테이너가 동작중인 것을 확인할 수 있습니다.
kubectl get pod
kc describe pod
# 방안1 : 파드의 복제본을 이용
kubectl debug $(kubectl get pod -l app=deploy-websrv -oname) -it --image=nicolaka/netshoot -c netdebug --share-processes --copy-to=websrv-debug --profile='sysadmin'
-----
ps axf
실습 - 유닉스 도메인 소켓 통신 확인
Bookinfo 실습 및 Istio 기능
먼저 Istio 실습을 위한 bookinfo 애플리케이션을 배포합니다.
Bookinfo 애플리케이션은 다음과 같은 참조관계를 가진 마이크로 서비스를 배포합니다.
- ProductPage 페이지에서 요청을 받으면, 도서 리뷰를 보여주는 Reviews 서비스와 도서 상세 정보를 보여주는 Details 서비스에 접속
- ProductPage는 Reviews와 Details 결과를 사용자에게 응답
- Reviews 서비스는 v1, v2, v3 세 개의 버전이 있고 v2, v3 버전의 경우 Ratings 서비스에 접속하여 도서에 대한 5단계 평가를 가져옴
- Reviews 서비스의 차이는, v1은 Rating 이 없고, v2는 검은색 별로 Ratings 가 표시되며, v3는 색깔이 있는 별로 Ratings 가 표시됨
# 모니터링
watch -d 'kubectl get pod -owide;echo;kubectl get svc'
# Bookinfo 애플리케이션 배포
echo $ISTIOV
cat ~/istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml
kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml
# 확인
kubectl get all,sa
# product 웹 접속 확인
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>"
# 로그
kubetail -l app=productpage -f
배포를 수행하면 다음과 같이 6개의 애플리케이션이 배포됩니다.
페이지에 진입하면 애플리케이션들의 로그를 확인할 수 있습니다.
빨간색은 프로덕트 페이지, 파란색은 이스티오 프록시(Envoy), 노란색이 init 로그입니다.
다음으로 Istio Gateway와 Virtual Service를 배포합니다.
# Istio Gateway/VirtualService 설정
cat ~/istio-$ISTIOV/samples/bookinfo/networking/bookinfo-gateway.yaml
kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/networking/bookinfo-gateway.yaml
# 확인
kubectl get gw,vs
istioctl proxy-status
# productpage 파드의 istio-proxy 로그 확인 Access log 가 출력 - Default access log format : 링크
kubetail -l app=productpage -c istio-proxy -f
접속 테스트를 해봅니다.
export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
echo $IGWHTTP
32759
# 접속 확인
kubectl get svc -n istio-system istio-ingressgateway
curl -s http://localhost:$IGWHTTP/productpage
다음으로 Kiali, Pormetheus, Grafana를 설치합니다.
Kiali는 Istio의 obervability 콘솔입니다.
kubectl apply -f ~/istio-$ISTIOV/samples/addons # 디렉터리에 있는 모든 yaml 자원을 생성
kubectl rollout status deployment/kiali -n istio-system
# 확인
kubectl get all,sa,cm -n istio-system
kubectl get svc,ep -n istio-system
# kiali 서비스 변경
kubectl patch svc -n istio-system kiali -p '{"spec":{"type":"NodePort"}}'
# kiali 웹 접속 주소 확인
KIALINodePort=$(kubectl get svc -n istio-system kiali -o jsonpath={.spec.ports[0].nodePort})
echo -e "KIALI UI URL = http://$(curl -s ipinfo.io/ip):$KIALINodePort"
# Grafana 서비스 변경
kubectl patch svc -n istio-system grafana -p '{"spec":{"type":"NodePort"}}'
# Grafana 웹 접속 주소 확인 : 7개의 대시보드
GRAFANANodePort=$(kubectl get svc -n istio-system grafana -o jsonpath={.spec.ports[0].nodePort})
echo -e "Grafana URL = http://$(curl -s ipinfo.io/ip):$GRAFANANodePort"
# Prometheus 서비스 변경
kubectl patch svc -n istio-system prometheus -p '{"spec":{"type":"NodePort"}}'
# Prometheus 웹 접속 주소 확인
PROMENodePort=$(kubectl get svc -n istio-system prometheus -o jsonpath={.spec.ports[0].nodePort})
echo -e "Prometheus URL = http://$(curl -s ipinfo.io/ip):$PROMENodePort"
Kiali 화면
Grafana 화면
Prometheus 화면
트래픽 관리는 Virtual service에서 세팅을 하고, gateway config로도 구성할 수 있습니다.
실습 - Request Routing
Request Routing은 쿠버네티스환경에서 라우팅을 동적으로 설정하는 기능입니다.
먼저, 특정 버전의 서비스로만 라우팅을 하도록 구성했습니다.
Destination Rule을 적용하면 이런 식으로 모든 서비스에 DestinationRule 이적용됩니다.
다음으로, header에 특정정보가 있을 경우 v2로, 그렇지 않을 경우 v1으로 전송하도록 Request라우팅을 수정해 보았습니다.
이런 식으로 Istio의 Request Routing을 이용하여 세부적인 트래픽 통제가 가능합니다.
실습 - Fault Injection
이번에는 의도적으로 통신 지연을 발생시켜서 애플리케이션의 동작상태가 어떻게 변화하는지 확인하는 것입니다.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
delay:
percentage:
value: 100.0
fixedDelay: 7s
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
# virtualservices 적용
kubectl apply -f virtual-service-ratings-test-delay.yaml
Kiali에 들어가서 모니터링을 해보면 다음과 같이 DC(DownstreamConnectionTermination) 에러가 발생하는 것을 확인할 수 있습니다.
이외에도 Kiali 상에서 자주 발생하는 종류의 오류들은 플래그 형태로 관리되고 있음을 도큐먼트에서 확인할 수 있습니다 - 링크
또한 특정조건에 다음과 같이 에러를 주입하여 오류 상황을 유발할 수 있습니다.
해당 실습에서는 jason이라는 접속자에게 의도적으로 100% 에러를 발생시킵니다.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
abort:
percentage:
value: 100.0
httpStatus: 500
# virtualservices 적용
kubectl apply -f virtual-service-ratings-test-abort.yaml
실습 - Traffic Shifting (카나리 배포 전략 활용)
카나리 배포 전략을 사용하기 적합한 가중치기반 라우팅 실습입니다.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10
# virtualservices 적용
kubectl apply -f virtual-service-reviews-90-10.yaml
이 외에도 여러 기능들을 제공하고 있습니다..
Istio 보안
제로 트러스트 (ZT: Zero Trust)는 ‘아무것도 신뢰할 수 없다는 가정하에, 사용자 및 다양한 정보를 바탕으로 최소한의 권한과 세밀한 통제를 지속적으로 수행하는 보안 활동’입니다. 이 컨셉에서는 내부자에 의해 내부 환경도 보안이 충분히 탈취될 수 있다고 가정하기 때문에 아무것도 신뢰할 수 없다고 가정하여 보안성을 높입니다.
서비스 매쉬환경을 구성하여 쿠버네티스 환경을 구성하면 기본적으로 Istio proxy 통신이 모두 내부적으로 mtls 통신을 하기 때문에 전체 제로 트러스트 컨셉의 통신 보안효율성을 높일 수 있습니다.
mtls 모드를 사용하면 envoy proxy 간 통신 시 mTLS를 사용하기 때문에 보안성이 강화됩니다.
아래의 그림은 Istio Mesh 내에서 서비스 간 통신 보안을 위한 인증서 발급 및 관리 과정을 보여줍니다.
- istiod는 gRPC 서비스를 제공하여 인증서 서명 요청(CSR)을 처리합니다.
- Istio-agent는 시작 시 개인 키와 인증서 서명 요청(CSR)을 생성하고, 이 CSR을 자신의 자격 증명과 함께 istiod로 전송합니다.
- istiod에 있는 인증 기관(CA)이 CSR에 포함된 자격 증명을 검증합니다. 검증이 성공하면, CA는 CSR을 서명하여 인증서를 발급합니다.
- 워크로드(서비스 A)가 시작되면, Envoy 프록시는 동일한 컨테이너에 있는 Istio 에이전트에게 인증서와 개인 키를 요청합니다. 이 요청은 Envoy의 gRPC기반 SDS API를 통해 이루어집니다.
- Istio 에이전트는 istiod로부터 받은 인증서와 개인 키를 Envoy에 전달합니다. 이 과정 역시 Envoy SDS API를 통해 수행됩니다.
- Istio 에이전트는 워크로드 인증서의 만료를 모니터링하며, 주기적으로 인증서와 개인 키를 갱신하기 위해 위의 과정을 반복합니다.
이런 식으로 Istio는 각 워크로드 간에 안전한 통신을 보장하며, 주기적인 인증서 갱신을 통해 보안 수준을 유지할 수 있게 됩니다.
또한 Istio는 두 가지 타입의 인증을 제공합니다.
- 피어 인증(Peer Authentication): 위에서 본 것처럼 mTLS 기반 서비스 간 인증으로, 연결을 시도하는 클라이언트를 검증하는 데 사용됩니다.
- 요청 인증(Request Authentication): JWT(JSON Web Token) 검증을 통해 요청 수준의 인증을 지원하며, 맞춤형 인증 제공자나 OpenID Connect 제공자를 사용할 수 있습니다.
실습 - Istio STRICT 모드 적용하기
먼저 일반 Pod를 배포합니다.
# 네임스페이스 생성
kubectl create ns test
# 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: netpod
namespace: test
spec:
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 확인 : sidecar 미적용
kubectl get pod -n test
# ratings 서비스 확인
kubectl get svc ratings
# ratings 접속 시도 : 성공
kubectl exec -it -n test netpod -- curl ratings.default:9080/health ;echo
{"status":"Ratings is healthy"}
# 로그 확인
kubetail -l app=ratings -f
다음으로 Istio의 PeerAuthentication을 Strict 모드로 설정하면 다음과 같이 일반적인 Http 통신은 실패하게 됩니다.
# Peer authentication 설정 변경 : PERMISSIVE(mTLS 사용/미사용 모두 허용) → STRICT(반드시 mTLS 사용, 미사용 시 거부)
cat <<EOF | kubectl create -f -
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default-strict
spec:
mtls:
mode: STRICT
EOF
# ratings 접속 시도 : 실패!
kubectl exec -it -n test netpod -- curl ratings.default:9080/health ;echo
curl: (56) Recv failure: Connection reset by peer
command terminated with exit code 56
kubetail -l app=ratings -f
[ratings-v1-b6994bb9-v6grn istio-proxy] [2022-02-14T20:36:39.136Z] "- - -" 0 NR filter_chain_not_found - "-" 0 0 0 - "-" "-" "-" "-" "-" - - 172.16.184.3:9080 172.16.116.4:49054 - -
실습 - HTTP Traffic
Http Traffic AuthorizationPolicy를 비워두면 통신이 되지 않습니다.
# The spec: field of the policy has the empty value {}. That value means that no traffic is permitted, effectively denying all requests
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-nothing
namespace: default
spec:
{}
EOF
# 확인
kubectl get AuthorizationPolicy
NAME AGE
allow-nothing 49s
kubetail -l app=productpage
[productpage-v1-6b746f74dc-dm4j6 istio-proxy] [2022-02-14T20:57:11.113Z] "GET /productpage HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "192.168.10.254" "curl/7.68.0" "3fdcdeb7-9cdb-9a38-8867-7d114590aacb" "www.gasida.dev:30959" "-" inbound|9080|| - 172.16.184.6:9080 192.168.10.254:0 outbound_.9080_._.productpage.default.svc.cluster.local -
# productpage 의 listeners 의 pc 정보 확인
istioctl pc listener $(kubectl get pod -l app=productpage -o jsonpath={.items[0].metadata.name}).default -o json
AuthorizationPolicy 적용을 통해 mTLS 통신이 가능하도록 설정합니다.
기존의 HTTP 통신은 다음과 같이 표시됩니다.
mTLS 통신 수행 시 자물쇠 모양이 kiali에 표출됩니다.
Istio 트래픽 흐름
Istio의 envoy 프록시를 통해 통신하게 되면 여러 가지 거치게 되는 통신 단계가 많아져 오버헤드가 많이 발생하게 됩니다.
외부 클라이언트 관점에서 통신하는 절차는 다음과 같습니다.
Pod로 진입하기 전(80)에 Istio-proxy(15006) 포트로 변경시키고 인보이 헤더를 추가하고, source IP를 127.0.0.6으로 변경한 다음 iptables 룰로 보냅니다. 이런 식으로 중간에서 트래픽을 가로챕니다.
도식의 해석이 어려워 ChatGPT의 힘을 빌어 트래픽 처리 과정 살펴봅니다.
1. 트래픽 유입 (Inbound TCP Traffic)
Pod로 들어오는 Inbound TCP 트래픽이 PREROUTING 체인에 도달합니다. 이 단계는 네트워크의 기본 트래픽 흐름을 나타냅니다.
2. PREROUTING에서 INPUT으로 전달
트래픽이 PREROUTING 체인을 통과한 후, INPUT 체인으로 전달됩니다. 여기서 트래픽이 들어오는지 확인하고 추가 처리를 진행합니다.
3. ISTIO_IN_REDIRECT 규칙 적용
ISTIO_IN_REDIRECT 규칙이 적용되어 Inbound 트래픽을 가로채서 내부적으로 Istio의 사이드카 프록시로 리다이렉트 합니다.
4. Sidecar Proxy로 리다이렉트
가로챈 트래픽이 Istio의 사이드카 프록시(Envoy)로 전달됩니다. 사이드카는 트래픽을 처리하거나 상위 서비스로 전송할 준비를 합니다.
5. POSTROUTING 체인
POSTROUTING 체인에서 트래픽이 나가는 방향으로 처리되기 전에 라우팅을 담당하는 과정입니다. 여기서도 트래픽 흐름을 확인하고 제어합니다.
6. ISTIO_INBOUND 규칙 처리
트래픽이 ISTIO_INBOUND 체인으로 이동합니다. 이는 가로채진 트래픽이 사이드카 프록시를 통과하기 위한 규칙입니다.
7. Inbound 트래픽 전달
사이드카 프록시는 Inbound 트래픽을 처리하고 애플리케이션 컨테이너로 전달합니다.
8. POSTROUTING 다시 경유
트래픽이 애플리케이션 컨테이너에서 나와 다시 POSTROUTING 체인을 통해 최종 전송 준비를 합니다.
9. OUTPUT 체인
트래픽이 OUTPUT 체인을 통해 나가게 됩니다. 이 단계는 트래픽이 컨테이너를 떠나기 전에 트래픽 경로를 결정하는 마지막 단계입니다.
10. Outbound 트래픽 처리 시작
Pod 내부에서 생성된 아웃바운드 트래픽이 나가게 되는 첫 번째 단계로, 이 트래픽이 Pod 외부로 나가기 위한 준비를 합니다.
11. ISTIO_OUTPUT 규칙 적용
ISTIO_OUTPUT 규칙을 통해 아웃바운드 트래픽이 다시 한번 가로채집니다. 이는 트래픽을 사이드카 프록시로 리다이렉트 하는 과정입니다.
12. ISTIO_REDIRECT 적용
ISTIO_REDIRECT 규칙이 적용되어 트래픽을 로컬의 사이드카 프록시로 리다이렉트 합니다. 이 프록시를 통해 트래픽 제어가 이루어집니다.
13. OUTPUT 체인으로 다시 전달
아웃바운드 트래픽이 OUTPUT 체인으로 다시 이동하며, 사이드카 프록시를 거쳐 상위 서비스로 전송될 준비를 합니다.
14. POSTROUTING 아웃바운드 처리
아웃바운드 트래픽이 POSTROUTING 체인으로 이동하여 Pod 외부로 나가는 마지막 단계에 들어갑니다.
15. 로컬 네트워크로 전달
트래픽이 ISTIO_REDIRECT를 통해 리다이렉트 되면서 로컬 네트워크로 전달되고, 실제로 상위 서비스 또는 외부 서비스로 나가기 위한 준비를 합니다.
16. 내부 트래픽 처리 (Internal Traffic)
내부적으로 처리되는 트래픽의 일부가 ISTIO_IN_REDIRECT로 리다이렉트 되는 규칙입니다. 이 단계는 iptables 규칙 설정에 대한 설명을 위한 용도로 다이어그램에서 표시되어 있으며, 라우팅 경로에는 직접 포함되지 않습니다.
실제로 외부 트래픽 로그를 확인해 보면 127.0.0.6으로 들어와서 192.168.10.200로 변경됩니다.
istio-init의 로그를 살펴보면 다음과 같이 iptables에 redirect를 시켜 트래픽 가로채기를 할 수 있게 합니다.
kubectl logs nginx-pod -c istio-init
실습으로 확인은 해보지 못했지만, 노드에 다음과 같은 정책이 세팅되어 있다고 합니다.
# 트래픽 인입 시 TCP 경우 모든 트래픽을 15006 으로 리다이렉트한다, 일부 포트는 제외(22, 15008, 15020, 15021, 15090)
c1 iptables -t nat -L -n -v
Chain PREROUTING (policy ACCEPT 44 packets, 2640 bytes)
pkts bytes target prot opt in out source destination
45 2700 ISTIO_INBOUND tcp -- * * 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_INBOUND (1 references)
pkts bytes target prot opt in out source destination
...
1 60 ISTIO_IN_REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_IN_REDIRECT (3 references)
pkts bytes target prot opt in out source destination
1 60 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 redir ports 15006
istio-proxy 컨테이너의 로그를 살펴보면 다음과 같이 출발지 IP를 127.0.0.6으로 변경하고 랜덤포트로 목적지로 전달하게 됩니다.
이 과정을 통해 실제 애플리케이션 컨테이너로 클라이언트 요청 트래픽이 전달됩니다.
나갈 때는 loopback 인터페이스가 받은 후 Istio-proxy에서 트래픽을 받고, source와 destination ip를 다시 원상태로 복구해서 통신이 됩니다.
파드가 외부로 통신을 할 때는
Destination 주소를 istio-proxy가 받을 수 있는 127.0.0.1:15001로 통신을 하고, 이를 Istio-proxt가 이것을 가로챈 다음 원래 목적지로 변경을 한 후 통신합니다.
Istio를 사용하게 되면 30% 정도의 컴퓨팅 리소스가 추가로 들어가는 이유가 이러한 통신구조 때문이라고 합니다. Sidecar 방식은 이렇게 복잡하기 때문에 노드 단위에서 zTunnel이 프락시 역할을 하도록 Ambient Mesh가 나왔다고 합니다..
마치며
Istio는 참으로 만든 분도, 쓰는 분도, 가르치는 분도 대단하다는 생각이 듭니다. 회사에서 Istio를 사용하여 진행한 프로젝트에서도 이런 식으로 딥다이브하게 접근하여 사용하지는 않았는데, 장애가 안 생긴 게 정말 다행이었군요..
아직 전체를 이해하진 못했지만, 어떤 식으로 흘러가는지 감을 잡을 수 있었던 시간이었던 것 같습니다. 실제로는 직접 하나하나의 과정을 그려봐야 완벽한 이해가 가능할 것 같아요.. Istio 이해 프로젝트를 하나 만들어야겠다는 생각이 듭니다 :).....
그래도 지난 6주의 과정을 통해 배경지식을 쌓아서 지금까지의 이해도를 가질 수 있는 것 같습니다. 스터디가 끝나고 개인적으로 조금 더 파봐야 할 부분이 아닐까 싶네요..
이상으로 이번주차 스터디 정리를 마치겠습니다.
긴 글 읽어주셔서 감사합니다 :)
'클라우드 컴퓨팅 & NoSQL > [KANS] 쿠버네티스 네트워크 심화 스터디' 카테고리의 다른 글
[9주차 - AWS EKS: VPC CNI] KANS 스터디 (24.10.27) (6) | 2024.11.03 |
---|---|
[8주차 - Cilium CNI] KANS 스터디 (24.10.20) (7) | 2024.10.25 |
[6주차 - Ingress & Gateway API] KANS 스터디 (24.10.06) (7) | 2024.10.13 |
[5주차 - Service : LoadBalancer] KANS 스터디 (24.09.29) (3) | 2024.10.05 |
[4주차 - Service : ClusterIP, NodePort] KANS 스터디 (24.09.22) (2) | 2024.09.26 |