Istio 개요
서비스 메쉬(Service Mesh)의 등장
서비스 메쉬의 필요성은 컨테이너 기술의 발전과 함께 부각되었습니다. 서비스가 점점 더 작아지고, 그 개수는 늘어나면서 전체 시스템을 모니터링하는 일이 점점 어려워졌습니다. 특히 서비스 운영 중 발생하는 장애나 병목의 원인을 파악하기 힘든 상황이 자주 발생하게 되었습니다. 그래서 이러한 문제들을 해결하기 위해 서비스 메쉬가 등장했습니다.
Istio란?
Istio는 오픈 소스 서비스 메쉬 솔루션으로, 마이크로서비스 아키텍처에서 통신과 관련된 복잡성을 줄여주는 도구입니다. Istio는 데이터 플레인과 컨트롤 플레인으로 구성되어 있으며, 데이터 플레인은 각 서비스 간의 트래픽을 제어하고, 컨트롤 플레인은 정책 관리와 설정을 담당합니다. 이를 통해 Istio는 네트워크 트래픽을 효율적으로 관리하고, 보안 인증, 로깅, 모니터링 등의 기능을 쉽게 적용할 수 있도록 지원합니다. Istio를 사용하면 개발자는 비즈니스 로직에 집중할 수 있으며, 인프라 관리 부담을 덜 수 있습니다.
Istio의 기본 구성
과거 Istio는 갤리(Galley), 시타델(Citadel), 파일럿(Pilot)과 같은 여러 컴포넌트로 나뉘어 있었는데, 이제 이러한 컴포넌트들은 모두 istiod로 통합된 것 같습니다.
istiod의 주요 기능은 다음과 같습니다.
- Discovery: 클러스터 내에 실행중인 서비스를 동적으로 연결하고 트래픽을 효율적으로 라우팅
- Configuration: 트레픽 제어 규칙, 서비스 통신 방식, 로드벨런싱 등 istio의 설정과 정책을 관리
- Certificates: 서비스 간, 안전한 통신을 보장하기 위해, 인증서 발급 및 관리
Sidecar or ambient
Istio는 두 가지 모드를 제공합니다.
1. Sidecar 모드
각 애플리케이션 Pod에 사이드카 프록시(Envoy)를 주입하여 트래픽을 제어하는 방식입니다. 모든 트래픽을 사이드카 프록시를 통해 통신하므로, 세밀한 제어와 모니터링을 할 수 있습니다. 하지만, 각 Pod마다 프록시를 추가하기 때문에, 리소스 소비가 증가하고, 관리의 복잡도가 증가합니다.
2. Ambient 모드
사이드카를 사용하지 않는 새로운 접근 방식으로, ztunnel이 각 노드에 배치되어 서비스간 네트워크 트래픽을 관리합니다. 사이드카의 주입이 필요 없기 때문에, 애플리케이션 변경 없이 메쉬를 적용할 수 있으며, 리소스 사용 측면에서 더 효율적입니다.
하지만, 사이드카 모드에 비해 세밀한 트레픽 제어나 보안 기능이 제한될 수 있습니다.
Sidecar 모드는 더 세밀한 보안과 제어가 필요한 경우 적합하고, Ambient 모드는 리소스 효율성을 중시하며 배포의 용이성을 원하는 경우 유리합니다. 이번 포스팅에서는 Sidecar 모드에 대해 다룹니다.
Envoy
Envoy란?
Envoy는 오픈소스 프록시 서버로, 고성능의 L4/L7 프록시로 마이크로서비스 간의 통신을 지원합니다. Envoy는 동적인 구성 변경을 지원하고, 설정 파일을 통해서 라우팅 규칙, 필터체인 등을 간편하게 관리할 수 있습니다. 또한 , xDS API를 지원하여, 서비스 메쉬, Gateway Api 등의 네트워크 서비스에서 핵심 역할을 하고 있습니다.
Istio에서의 Envoy 활용
Istio는 각 애플리케이션 파드에 사이드카로 Envoy 프록시(Istio-proxy)를 주입하여 모든 인바운드 및 아운 바운드 트래픽을 가로챕니다. 이를 통해서 트래픽 관리, 로드벨런싱, 보안 정책 등을 수행할 수 있습니다.
또한, Envoy가 제공하는 xDS Sync API를 이용해서 다양한 네트워크 설정을 동적으로 구성할 수 있습니다.
Envoy의 xDS Sync API는 아래와 같은 레이어에서 동작하게 됩니다.
Layer | Description |
---|---|
CDS (Cluster Discovery Service) | 업스트림 서비스와의 연결을 위한 클러스터 정의를 관리합니다. |
EDS (Endpoint Discovery Service) | 클러스터 내 엔드포인트(특정 인스턴스)를 관리하여 동적 서비스 발견을 가능하게 합니다.. |
LDS (Listener Discovery Service) | 특정 포트에서 Envoy가 수신 연결을 처리하는 리스너를 구성합니다 |
RDS (Route Discovery Service) | HTTP 요청의 라우팅 규칙을 정의하여 요청이 클러스터로 전달되는 방식을 결정합니다. |
SDS (Secret Discovery Service) | TLS 인증서와 같은 보안 자격 증명을 관리하여 안전한 통신 구성을 제공합니다. |
Envoy 실습
Envoy 프록시를 설치하고, 간단한 설정을 통해 Demo 페이지를 확인해 봅니다.
먼저, Enovy porxy를 설치합니다.
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> **jammy** 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 공식문서에서 제공하는 Demo 설정을 살펴봅니다.
curl -O <https://www.envoyproxy.io/docs/envoy/latest/_downloads/92dcb9714fb6bc288d042029b34c0de4/**envoy-demo.yaml**>
envoy -c envoy-demo.yaml
서비스가 Listen 할 IP 주소와 포트, 다양한 라우팅 설정이 포함되어 있습니다.
envoy 설정파일을 적용합니다.
envoy -c envoy-demo.yaml
위에서 설정한 10000번 TCP 포트가 Listen인 것을 확인할 수 있고, 브라우저를 통해서 Demo 페이지에 접속도 가능합니다.
Envoy는 동적 설정 기능을 통해 아래 설정을 오버라이드 적용하여 9092 포트(관리자 페이지)의 서비스도 열어줍니다
# envoy-override.yaml
admin: listeners:
address:
socket_address:
address: 0.0.0.0
port_value: 9902
# 위 설정을 override하여 실행
envoy -c envoy-demo.yaml --config-yaml "$(cat envoy-override.yaml)"
이를 통해 9092 포트에서 관리자 페이지를 확인할 수 있습니다.
이와 같이 Envoy의 동적 설정 파일을 활용하면, 환경 변화에 즉각적으로 대응할 수 있어 마이크로서비스 아키텍처의 유연성을 높일 수 있습니다.
Istio 기본 사용
Istio 설치
Istio는 Istioctl, Helm, Operator 등 다양한 설치 방법을 지원합니다. 1.23.x 버전부터는 Operator 방식은 Deprecated 된다고 하여, 앞으로는 Helm 방식으로 설치하도록 권장하는 것 같습니다. 이번 실습에서는 가장 일반적으로 사용되는 Istioctl을 통해 설치하겠습니다.
Istio는 다양한 Profile을 지원하고 있습니다. 이번 실습에는 Demo Profile로 지정하여 설치를 진행합니다.
쿠버네티스 컨트롤 플레인 노드에서 istioctl을 설치합니다.
# 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
실습 복잡도를 줄이기 위해 demo profile을 선택하고, egressGateway는 비활성화를 하겠습니다.
# 설정파일 생성
istioctl profile dump demo > demo-profile.yaml
# demo-profile.yaml: egressgateway -> false
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
components:
base:
enabled: true
egressGateways:
- enabled: false
name: istio-egressgateway
ingressGateways:
- enabled: true
name: istio-ingressgateway
pilot:
enabled: true
hub: docker.io/istio
profile: demo
tag: 1.23.2
values:
defaultRevision: ""
gateways:
istio-egressgateway: {}
istio-ingressgateway: {}
global:
configValidation: true
istioNamespace: istio-system
profile: demo
# istio 설치
istioctl install -f demo-profile.yaml -y
Istio의 주요 리소스(istiod, istio-ingressgateway)를 확인합니다
(⎈|default:N/A) root@k3s-s:~# k -n istio-system get all
NAME READY STATUS RESTARTS AGE
pod/istio-ingressgateway-5f9f654d46-mwc69 1/1 Running 0 2m32s
pod/istiod-7f8b586864-bw9zh 1/1 Running 0 2m40s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/istio-ingressgateway LoadBalancer 10.10.200.85 192.168.10.10,192.168.10.101,192.168.10.102 15021:31095/TCP,80:30515/TCP,443:30841/TCP,31400:32357/TCP,15443:30355/TCP 2m31s
service/istiod ClusterIP 10.10.200.203 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 2m40s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/istio-ingressgateway 1/1 1 1 2m32s
deployment.apps/istiod 1/1 1 1 2m40s
**(⎈|default:N/A) root@k3s-s:~# k -n istio-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 10.10.200.85 192.168.10.10,192.168.10.101,192.168.10.102 15021:31095/TCP,80:30515/TCP,443:30841/TCP,31400:32357/TCP,15443:30355/TCP 2m58s
istiod ClusterIP 10.10.200.203 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 3m7s
(⎈|default:N/A) root@k3s-s:~# kubectl get crd | grep istio.io | sort
authorizationpolicies.security.istio.io 2024-10-19T07:16:31Z
destinationrules.networking.istio.io 2024-10-19T07:16:31Z
envoyfilters.networking.istio.io 2024-10-19T07:16:31Z
gateways.networking.istio.io 2024-10-19T07:16:31Z
peerauthentications.security.istio.io 2024-10-19T07:16:31Z
proxyconfigs.networking.istio.io 2024-10-19T07:16:31Z
requestauthentications.security.istio.io 2024-10-19T07:16:31Z
serviceentries.networking.istio.io 2024-10-19T07:16:31Z
sidecars.networking.istio.io 2024-10-19T07:16:31Z
telemetries.telemetry.istio.io 2024-10-19T07:16:31Z
virtualservices.networking.istio.io 2024-10-19T07:16:31Z
wasmplugins.extensions.istio.io 2024-10-19T07:16:31Z
workloadentries.networking.istio.io 2024-10-19T07:16:32Z
workloadgroups.networking.istio.io 2024-10-19T07:16:32Z**
사이드카 인젝션(Sidecar Injection)
Istio에서 Sidecar Injection 기능은 각 애플리케이션 파드에 추가적인 istio-proxy(Envoy) 컨테이너를 자동으로 추가하는 방법입니다
사이드카 인젝션 적용 방법은 다음과 같습니다.
# istioctl 명령어
istioctl kube-inject -f $POD.yaml
# 네임스페이스에 injection 라벨 적용
kubectl label namespace default istio-injection=enabled
kubectl get ns -L istio-injection
파드 배포 후 사이드카 인젝션을 확인합니다. 파드의 Ready 상태가 2/2이고, 컨테이너 상세 정보를 보면 istio-proxy 컨테이너를 확인할 수 있습니다.
kubectl run nginx --image=nginx:alpine -n default
(⎈|default:N/A) root@k3s-s:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 2/2 Running 0 2m9s
(⎈|default:N/A) root@k3s-s:~# k describe pod nginx | grep -i -C3 container
Containers:
nginx:
Container ID: containerd://4820607bffca2735f567c420e4850eac5c92709845a2ce2e052ef5b0750ab114
Image: nginx:alpine
Image ID: docker.io/library/nginx@sha256:2140dad235c130ac861018a4e13a6bc8aea3a35f3a40e20c1b060d51a7efd250
Port: <none>
--
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-p2p8t (ro)
istio-proxy:
Container ID: containerd://8390354a6b8603a012c0f82c2573b9feafe770e06e59e3c4ad6d71a714e62370
Image: docker.io/istio/proxyv2:1.23.2
Image ID: docker.io/istio/proxyv2@sha256:2876cfc2fdf47e4b9665390ccc9ccf2bf913b71379325b8438135c9f35578e1a
Port: 15090/TCP
...
네임스페이스의 injection 라벨을 설정할 경우, 해당 네임스페이스에서 파드 생성 시, 모든 파드가 자동으로 인젝션 됩니다. 하지만, 기존에 만들어진 파드들은 재시작을 해야 적용되기 때문에, 주의하도록 합니다.( Ambient 모드는 이러한 단점을 보완 )
Sidecar Injection의 동작원리는 다음과 같습니다.
Istio는 Kubernetes의 Mutating Admission Webhook을 사용하여 사이드카 인젝션을 수행합니다. 이 웹훅은 새로운 파드가 생성될 때마다 호출되어, 파드의 정의를 수정하여 사이드카 컨테이너를 추가합니다.
Istio 서비스 접속 및 외부 노출
Istio의 Istio-Ingressgateway(Gateway,Virtual Service)를 활용하여 웹서버 외부노출 하기
기존 쿠버네티스에서 Ingress를 사용할 때, 외부로 노출하기 위해서는 Ingress-controller가 필요합니다. Istio에서도 외부로부터 트래픽을 내부로 라우팅 하고 진입점 역할을 하는 게이트웨이가 필요합니다.
Istio에서는 아래 두 가지 방법을 지원합니다.
1. Istio-ingressgatway
Istio가 제공하는 게이트웨이로, Gateway(Istio), VirtualService 등 Istio가 제공하는 특화된 기능을 사용할 수 있습니다.
2.Kubernetes Gateway API
지난 포스팅에서 다룬 Gateway API입니다. Kubernetes에서 공식으로 제공하는 Gateway(Kubernetes), HTTPRoute를 사용하기 때문에, 범용적이지만, 아직, Istio에 비해 기능은 부족한 것 같습니다.
특징 | Istio-ingressgateway | Gateway API |
---|---|---|
설정 방식 | Istio의 Gateway , VirtualService 리소스를 통해 설정 |
Kubernetes의 Gateway , HTTPRoute 등을 사용 |
확장성 | Istio에 특화된 기능 제공 | 더 범용적이고 확장 가능한 구조 |
트래픽 관리 | Istio의 Envoy 기반 강력한 라우팅 제어 | 여러 솔루션과 호환 가능한 표준화된 라우팅 |
유연성 | Istio 내부 환경에 최적화된 기능 | 멀티 프로토콜, 멀티 클러스터에 적합 |
Istio의 공식문서에서도, 아래와 같이 두 가지 유형의 가이드를 제공합니다.
Istio 서비스 메쉬까지 등장하면서, 학습 시에, 네트워크 리소스의 용어와 역할이 너무 헷갈려서 아래와 같이 정리해보았습니다.
이번 실습에서는 Istio 관련 내용을 다루기 때문에, istio-ingressgateway를 사용해 보도록 하겠습니다.
ingressgateway의 서비스를 노드포트로 변경하여 외부에서 접속하도록 합니다.
# istio-ingressgateway 서비스 NodePort로 변경
kubectl patch **svc** -n istio-system **istio-ingressgateway** -p '{"spec":{"type":"NodePort"}}'
root@k3s-s:~# k get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
istio-ingressgateway NodePort 10.10.200.85 <none> 15021:31095/TCP,80:30515/TCP,443:30841/TCP,31400:32357/TCP,15443:30355/TCP 139m
istiod ClusterIP 10.10.200.203 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP
편리한 실습을 위해, 접속할 도메인 및 포트를 변수로 지정합니다.
#Istio 접속 테스트를 위한 변수 지정*
export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
echo $IGWHTTP
30515 # ingressgateway Service의 Nodeport
export MYDOMAIN=www.hackjap.com
echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile
웹서버(nginx)를 생성합니다.
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: kans-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-websrv
spec:
replicas: 1
selector:
matchLabels:
app: deploy-websrv
template:
metadata:
labels:
app: deploy-websrv
spec:
serviceAccountName: kans-nginx
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
root@k3s-s:~# kubectl get pod,svc,ep,sa -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/deploy-websrv-778ffd6947-zqpmz 2/2 Running 0 72m 172.16.2.4 k3s-w2 <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.10.200.1 <none> 443/TCP 3h57m <none>
service/svc-clusterip ClusterIP 10.10.200.86 <none> 80/TCP 72m app=deploy-websrv
...
Gateway와 Virtual 서비스를 배포합니다.
- Gateway: 내부에서 통신할 도메인과 TCP 프로토콜 정의
- VirtualService : test-gateway를 연결하고, 라우팅 룰을 정의
cat <<EOF | kubectl create -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: test-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: nginx-service
spec:
hosts:
- "$MYDOMAIN"
gateways:
- test-gateway
http:
- route:
- destination:
host: svc-clusterip
port:
number: 80
EOF
# 확인
kubectl get gw,vs
NAME AGE ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gateway.networking.istio.io/test-gateway 81m
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/nginx-service ["test-gateway"] ["www.hackjap.com"] 81m
root@k3s-s:~# istioctl proxy-status # 단축어 ps
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
deploy-websrv-778ffd6947-zqpmz.default Kubernetes SYNCED (23m) SYNCED (23m) SYNCED (23m) SYNCED (23m) IGNORED istiod-7f8b586864-bw9zh 1.23.2
istio-ingressgateway-5f9f654d46-mwc69.istio-system Kubernetes SYNCED (26m) SYNCED (26m) SYNCED (26m) SYNCED (26m) IGNORED istiod-7f8b586864-bw9zh 1.23.2
이제 외부에서 서비스가 접속되는 것을 확인할 수 있습니다.
# 서비스 접속
curl -s $MYDOMAIN:$IGWHTTP | grep -o "<title>.*</title>"
<title>Welcome to nginx!</title>
# 로그 확인
root@k3s-s:~# kubetail -n istio-system -l app=istio-ingressgateway -f
Will tail 1 logs...
istio-ingressgateway-5f9f654d46-mwc69
[istio-ingressgateway-5f9f654d46-mwc69] [2024-10-19T09:46:46.121Z] "GET / HTTP/1.1" 200 - via_upstream - "-" 0 615 1
1 "172.16.0.1" "curl/7.81.0" "f6248750-1546-96ee-b2ad-55ab09275bf2" "www.hackjap.com:30515" "172.16.2.4:80" outbound|80||svc-clusterip.default.svc.cluster.local 172.16.0.4:42430 172.16.0.4:8080 172.16.0.1:47688 - -
TODO : @RDS/LDS
MSA 애플리케이션 실습(Bookinfo)
istio에서 제공하는 Sample 예제로, 4종 MSA 애플리케이션으로 구성되어 있습니다.
Bookinfo 애플리케이션 배포
istioctl 설치 시, 사용한 디렉터리에서 Bookinfo 애플리케이션을 배포합니다.
# 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>"
Istio Gateway와 VirtualService를 설정합니다.
# 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
root@k3s-s:~# k get gw,vs
NAME AGE
gateway.networking.istio.io/bookinfo-gateway 84s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/bookinfo ["bookinfo-gateway"] ["*"] 84s**
productpage에 접속합니다. 각 화면의 영역별로 다른 서비스를 호출하고 있습니다.
Istio Monitoring
Istio는 MAS 간 네트워크 트래픽을 관리하기 때문에 모니터링이 필히 중요합니다. 다양한 모니터링 도구를 Addon 형태로 제공하여, 네트워크의 가시성과 운영 효율을 높일 수 있습니다.
주요 Addon은 다음과 같습니다.
- 키알리(Kiali): 대시보드각 서비스 간 연결 상태, 트래픽, 에러율을 쉽게 볼 수 있어, 장애 지점을 시각적으로 분석할 수 있습니다.
- 서비스 메쉬 내에서 서비스 간 네트워크 관계를 시각적으로 보여주는 대시보드입니다.
- 프로메테우스(Prometheus): 서비스 간 요청 수, 지연시간, 에러율, 네트워크 트래픽 등을 실시간으로 모니터링할 수 있습니다.
- Istio가 관리하는 각 서비스 간의 메트릭을 자동 수집합니다
- 그라파나(Grafana): Istio가 제공하는 미리 구성된 대시보드를 통해 네트워크의 상태를 한눈에 파악할 수 있습니다.
- Prometheus에서 수집된 데이터를 기반으로 시각적인 대시보드를 제공합니다.
모니터링 Addon 설치
# Addon 배포(Kiali,Prometheus,Grafana)
tree ~/istio-$ISTIOV/samples/addons/
kubectl apply -f ~/istio-$ISTIOV/samples/addons # 디렉터리에 있는 모든 yaml 자원을 생성
kubectl rollout status deployment/kiali -n istio-system
# 확인
root@k3s-s:~# kubectl get pod -n istio-system
NAME READY STATUS RESTARTS AGE
grafana-7f76bc9cdb-wddkh 1/1 Running 0 49s
istio-ingressgateway-5f9f654d46-mwc69 1/1 Running 0 3h43m
istiod-7f8b586864-bw9zh 1/1 Running 0 3h43m
jaeger-66f9675c7b-l6mv9 1/1 Running 0 49s
kiali-65c46f9d98-627gv 1/1 Running 0 48s
loki-0 0/1 Running 0 48s
prometheus-7979bfd58c-tpznj 2/2 Running 0 48s
외부 접속을 위해 해당 서비스들을 NodePort로 변경합니다.
# 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"
모니터링 웹 접속
Traffic Management(트래픽 관리)
Istio에서 지원하는 다양한 트래픽 관리에 대해 다뤄봅니다.
Istio에서 트래픽 제어는 VirtualService
와 DestinationRule
설정을 통해서 동작합니다.
DestinationRule
에서 subset
은 트래픽을 특정 서비스 버전(또는 가중치 그룹)으로 라우팅 하기 위해 정의된 설정입니다. 이를 통해 Istio는 같은 서비스 내에서도 특정 라벨을 가진 파드(ex: 특정 버전의 파드)에 트래픽을 분배할 수 있습니다.
아래와 같이 정의한 subset을 VirtualService에서 사용할 수 있습니다.
실습 전 기본 DestinatnioRule을 적용하고, 각각 파드에 subset을 지정합니다.
# 샘플 파일들 확인
cd ~/istio-$ISTIOV/samples/bookinfo/networking
tree
# 기본 DestinationRule 적용
kubectl apply -f destination-rule-all.yaml
# DestinationRule 확인 dr(=destinationrules) : KIALI Services 확인 시 GW, VS, DR 확인
kubectl get dr
NAME HOST AGE
details details 16m
productpage productpage 16m
ratings ratings 16m
reviews reviews 16m
Requset Routing
특정 조건에 따른 라우팅 : URL Path, Header, Method 등에 따라 특성 서비스 라우팅
Case 1: 4개의 서비스 모두 v1의 서브셋(subset)에 전송
모든 트래픽을 v1 버전으로 설정된 서브셋으로 전송하여, 안정적인 버전을 유지하며 서비스의 일관성을 보장합니다.
# kubectl apply -f virtual-service-all-v1.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: productpage
spec:
hosts:
- productpage
http:
- route:
- destination:
host: productpage
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: details
spec:
hosts:
- details
http:
- route:
- destination:
host: details
subset: v1
---
모든 요청이 v1 라벨의 파드로 라우팅 됨을 확인할 수 있습니다.
Case 2: 특정 사용자의 요청만 v2의 서브셋(subset)에 전송end-user
헤더가 jason인 경우, reviews 서비스의 v2 버전으로 라우팅 합니다. 조건을 만족하지 않은 요청은 reviews
서비스의 v1
버전으로 라우팅 됩니다.
# virtual-service-reviews-test-v2.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
요청 서비스의 end-user
헤더가 jason인 경우, v2 버전으로 라우팅 하여, 검은색 별 모양의 리뷰 화면을 확인할 수 있습니다.
Fault Injection
HTTP 결함 주입 - 의도적으로 지연(Latency)을 발생시켜 중단
Case 1: end-user 가 jason는 ratings v1에 7초 지연 발생, 그 외 사용자는 ratings v1 정상 연결
# virtual-service-ratings-test-delay.yaml
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
end-user
헤더가 jason인 경우, reviews 서비스의 v1 버전에 7초 지연이 발생합니다. 조건에 만족하지 않는 요청은 정상연결됩니다.
Case 2: end-user 가 jason 는 ratings v1 에 500 에러 리턴, 그외 사용자는 ratings v1 정상 연결
# virtual-service-ratings-test-abort.yaml
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
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
end-user
헤더가 jason인 경우, reviews 서비스의 v1 버전에 장애를 발생합니다. 조건에 만족하지 않는 요청은 정상연결 됩니다.
Traffic Shifting
트래픽 전환 - 카나리 배포 전략 등에 활용
Case 1: v1과 v3 버전의 Reviews 서비스에 트래픽을 50:50 비율로 분산 라우팅
# virtual-service-reviews-50-v3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 50
- destination:
host: reviews
subset: v3
weight: 50
v1과 v3 버전의 Reviews 서비스에 트래픽을 50:50 비율로 분산 라우팅되는 것을 확인할 수 있습니다.
Case 2: v1과 v2 버전의 Reviews 서비스에 트래픽을 80:20 비율로 분산 라우팅
# virtual-service-reviews-80-20.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 80
- destination:
host: reviews
subset: v2
weight: 20
v1과 v2 버전의 Reviews 서비스에 트래픽을 80:20 비율로 분산 라우팅되는 것을 확인할 수 있습니다.
Requset Timeouts
타임아웃 제어 - 요청에 대한 응답 시간을 설정된 시간 내로 제한
Case 1 : Ratings 서비스 호출 시 2초 지연 설정 및 v1 서비스 호출
Ratings 서비스 호출에 2초 지연을 추가해 장애 상황을 시뮬레이션합니다.
# reviews service 는 reviews v2 만 호출 설정
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v2
EOF
# ratings service 호출 시 2초 지연 설정 >> 가장 끝단에서 장애 상황이라고 재연 >> 앞단의 모든 서비스가 영향을 받는다
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percent: 100
fixedDelay: 2s
route:
- destination:
host: ratings
subset: v1
EOF
타임아웃 설정이 없는 경우 앞단의 모든 서비스가 영향을 받을 수 있지만, 타임아웃을 설정하면 지연된 서비스의 문제를 일정 시간 후에 차단함으로써 영향이 최소화될 수 있습니다.
Security(보안)
Istio는 마이크로서비스 간의 통신을 안전하게 보호하기 위해 다양한 보안 기능을 제공합니다.
- 트래픽 암호화(Encryption)
- 서비스 간 통신을 암호화(mTLS)하여 안전한 통신을 보장합니다.
- 접근 제어(Access Control)
- 각 서비스에 대한 세밀하고 유연한 서비스를 접근제어를 제공합니다(코드 수정 없이도 가능)
- 감사(Audit)
- 누가, 언제 무엇을 했는지 추적하기 위해 Istio는 감사 도구를 제공합니다.
Istio의 도입으로 “제로 트러스트” 솔루션을 구축할 수 있습니다.
SPIFFE - 워크로드 식별 표준 집합
- SPIFFE는 서비스 간 인증을 위한 표준을 제공하는 오픈소스입니다.
- Istio는 SPIFFE를 기본 인증 메커니즘으로 사용하여 서비스 간 신뢰성을 보장합니다.
- Istio는 각 서비스에 SVID(SPIFFE ID)를 할당하고, 이 SVID를 바탕으로 mTLS를 사용해 서비스를 암호화합니다.
spiffe://cluster.local/ns/default/sa/bookinfo-productpage…
SPIFFE는 CNCF의 Graduation Project 중에서 전혀 감이 안 오는 프로젝트로 기억하고 있었는데, 이제야 궁금증이 풀립니다..
Authentication
사용자와 서비스 간의 신원을 확인하는 과정입니다. Istio는 mTLS(전송 계층 보안) 및 JWT(JSON Web Token)을 이용해 트래픽의 신원을 검증하고, 신뢰할 수 있는 통신을 형성합니다.(사용자가 누구인지?)
Istio는 아래 2가지 유형의 인증(Authentication)을 제공합니다.
1.Peer Authentication(Auto mTLS)
서비스 간 트래픽을 인증하는 방식으로, 주로 mTLS를 사용하여 클러스터 내 서비스 간 신뢰성을 보장합니다.
PeerAuthentication 실습
먼저, Client 파드로 net-pod를 test, namespace 파드에 각각 배포합니다.
test 네임스페이스는 사이드카 인젝션 설정이 되어있지 않기 때문에, 해당 파드의 컨테이너는 1개로 동작합니다.
# test, default 네임스페이스에 net-pod 배포
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
---
apiVersion: v1
kind: Pod
metadata:
name: netpod
namespace: default
spec:
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# net-pod 확인
(⎈|default:N/A) root@k3s-s:~# k get pod -A | grep net
default netpod 2/2 Running 0 3m18s
test netpod 1/1 Running 0 3h15m
두 네임스페이스 파드에서 rating 서비스로 요청을 보내면 정상적으로 통신됨을 확인할 수 있습니다.
이제, PeerAuthentication 적용합니다. mTLS 활성화를 위해 STRICT 모드로 설정합니다.
3가지 mTLS 모드를 제공합니다.
- DISABLE: mTLS를 사용하지 않음.
- PERMISSIVE: mTLS와 평문 HTTP 둘 다 허용.
- STRICT: mTLS만 허용
cat <<EOF | kubectl create -f -
apiVersion: security.istio.io/v1beta1
kind: **PeerAuthentication**
metadata:
name: default-strict
spec:
**mtls**:
mode: **STRICT**
EOF
Istio에서 mTLS를 활성화하면 사이드카 인젝션이 된 파드는 mTLS 통신을 통해 서로 안전하게 통신할 수 있지만, 반면에 파드는 사이드카 인젝션 되지 않은 test 네임스페이스의 파드는 트래픽이 차단되는 것을 확인할 수 있습니다.
kiali 대시보드에서도 mTLS 통신을 확인할 수 있습니다.
개인적으로, 처음부터 STRICT 모드를 사용하기보다는, Permissive 모드(mTLS를 지원하는 서비스와 그렇지 않은 서비스를 모두 지원하는 방식)를 사용해서, 클러스터 내의 통신 보안 수준을 점진적으로 높이는 것이 중요하다고 생각합니다.
2.Request Authentication(JWT)
개별 요청을 인증하는 방식으로, 주로 JWT를 이용해 사용자나 클라이언트가 신뢰할 수 있는지를 검증합니다.
PeerAuthentication 실습 ( JWT 동작원리에 대한 내용은 생략하였습니다.)
JWT는 Authrozation 헤더에 Bearer 토큰 형식으로 유효성을 검사하기 때문에, httpbin 파드를 통해 실습 환경을 구성합니다. → 클라이언트(sleep), 서버(httpbin)
사이드카 인젝션(istio kube-inject) 적용하여 배포합니다.
wget -f <https://raw.githubusercontent.com/istio/istio/release-1.20/samples/httpbin/httpbin.yaml>
kubectl apply -f <(istioctl kube-inject -f httpbin.yaml) -n foo
kubectl apply -f <(istioctl kube-inject -f ~/istio-$ISTIOV/samples/sleep/sleep.yaml) -n foo
RequestAuthentication 설정을 적용합니다. → jwt 정보 주입
# 배포
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: **RequestAuthentication**
metadata:
name: "jwt-example"
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
**jwtRules**:
- issuer: "testing@secure.istio.io"
**jwksUri**: "<https://raw.githubusercontent.com/istio/istio/release-1.13/security/tools/jwt/samples/jwks.json>"
EOF
# 확인
kubectl get requestauthentications.security.istio.io -n foo
NAME AGE
jwt-example 4s
# json 파일 내용
cat jwks.json | jq
{
"keys": [
{
"e": "AQAB",
"kid": "DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ",
"kty": "RSA",
"n": "xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"
}
]
}
httpbin 파드로 서비스 요청 시, HTTP 요청 헤더에 잘못된 토큰(Invalid Token)을 전달하면 401(Unauthorized) 코드가 응답합니다.
kubectl exec $SLEEP_POD -c sleep -n foo -- curl "<http://httpbin.foo:8000/headers>" -sS -o /dev/null -H "Authorization: Bearer invalidToken" -w "%{http_code}\\n"
401
AuthorizationPolicy 설정을 통해서, JWT의 올바른 신원 (testing@secure.istio.io)을 확인하고 허용하도록 합니다.
# 배포
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: require-jwt
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
action: **ALLOW**
rules:
- from:
- source:
requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
EOF
이제 Bearer 토큰을 추출하여, 헤더에 포함하여 서비스로 요청하면 정상적으로 인증이 되는 것을 확인할 수 있습니다.
# Bear 토큰 발급
TOKEN=$(curl <https://raw.githubusercontent.com/istio/istio/release-1.13/security/tools/jwt/samples/demo.jwt> -s)
echo "$TOKEN"
eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg
echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode -
{"exp":4685989700,"foo":"bar","iat":1532389700,"iss":"testing@secure.istio.io","sub":"testing@secure.istio.io"}
# 발급받은 Token으로 서비스 요청
kubectl exec $SLEEP_POD -c sleep -n foo -- curl "<http://httpbin.foo:8000/headers>" -sS -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{http_code}\\n"
200
Authorization
Istio의 Authorization은 AuthenticationPolicy
를 사용하여 요청에 대한 접근 권한을 제어합니다.
AuthoriationPolicy
특정 서비스, 사용자, 요청 속성 등을 기반으로 허용(Allow) 또는 거부(Deny) 정책을 설정할 수 있습니다
정책 적용 우선순위는 DENY > ALLOW입니다.
AuthoriationPolicy 실습
- 모든 정책 차단
{}
명시하면, 어떠한 요청도 허용하지 않는 정책을 의미합니다.
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: **AuthorizationPolicy**
metadata:
name: allow-nothing
namespace: default
spec:
**{}**
EOF
서비스로 요청 시, 403(Authorization)을 반환합니다.
- HTTP 특정 메서드 허용
app: productpage
레이블을 가진 파드에 대해 HTTP GET 요청만 허용합니다.
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: "productpage-viewer"
namespace: default
spec:
selector:
matchLabels:
app: **productpage**
action: **ALLOW**
rules:
- to:
- operation:
methods: ["**GET**"]
EOF
productpage는 200(ok)이지만, reviews/details 는 403 에러를 확인할 수 있습니다.
정리하자면, RequestAuthentication을 먼저 사용해 요청의 신원을 확인한 후, AuthorizationPolicy를 통해 인가를 설정하여 특정 요청만 허용하는 방식으로 두 정책이 협력하여 보안을 제공합니다.
따라서, 보안 관련 설정을 Istio에서 정의하고 적용함으로써 코드 수정 없이 서비스의 보안 수준을 크게 향상할 수 있다는 큰 장점이 있습니다.
Istio 트래픽 흐름 분석
작성예정.
'Kubernetes > Network' 카테고리의 다른 글
AWS EKS VPC CNI - KANS 9주차 (2) | 2024.11.02 |
---|---|
eBPF & Cilium CNI - KANS 8주차 (3) | 2024.10.27 |
Ingress와 Kubernetes Gateway Api - KANS 6주차 (0) | 2024.10.13 |
kube-proxy IPVS 모드의 동작 원리 이해 - KANS 5주차 2 (1) | 2024.10.06 |
MetalLB를 활용한 LoadBalancer 서비스의 동작 원리 이해 - KANS 5주차 1 (2) | 2024.10.06 |