[Study 7주차] 쿠버네티스 Service Mesh - Envoy Proxy, Istio
- -
CoudNet@ 팀의 가시다님께서 리딩하시는 KANS Study (Kubernetes Advanced Networking Study) 7주차 스터디 내용 정리
이번 주차에서는 쿠버네티스 서비스 메시에 대해서 공부하였습니다.
그 중, 이스티오(Istio) 와 엔보이(Envoy) 에 대해서 동작 방식과 구성에 대해 집중적으로 공부했습니다.
1. Service Mesh
서비스 메시는 단어를 직역하면 서비스가 그물망 형태로 이루어져 있는 것을 의미합니다.
이는 서비스 어플리케이션들 간의 네트워크 통신이 무수히 많아지는 형태,
즉 마이크로서비스 아키텍처 환경에서의 서비스 간 통신을 제어하고 모니터링 하기 위해 생겨난 개념입니다.
1. 기존 통신 환경
가장 기본적인 어플리케이션 간 통신은 직접 통신입니다.
이런 어플리케이션 통신이 많아진다면 네트워크를 어떻게 모니터링하고 제어할 수 있을까요??
2. Proxy 통신 환경
서비스 메시를 구현하기 위해 나온 아이디어가 바로 Proxy 통신 방식입니다.
기존에 어플리케이션이 하던 직접 통신 패킷을 Proxy 가 가로채어 대신 통신하는 것입니다.
이 경우 Proxy 를 통해 어플리케이션 간 통신을 안되게 할 수도 있고, 어떤 통신이 이루어지고 있는 지 알 수 있게 됩니다.
이러한 방식을 사이드카 패턴이라고 하며, 해당 Proxy 구현 도구로 Envoy Proxy 가 있습니다.
3. Proxy 중앙 관리 환경
하지만 Proxy 를 직접 하나씩 관리하긴 어려우니 중앙 집중 관리 시스템이 필요합니다.
이를 위해서 Proxy 를 중앙에서 관리할 수 있는 API 를 가진 도구가 필요하게 되는데,
서비스 메시 구현체인 이스티오 (Istio) 가 이러한 동작을 합니다.
중앙 관리 환경에서는 아래와 같은 설정을 관리할 수 있어야 합니다.
a. 트래픽 모니터링
- 요청의 에러율, 레이턴시, 커넥션 개수, 요청 개수 등의 메트릭 모니터링
b. 트래픽 컨트롤
- 트래픽 시프팅 (Traffic Shifting)
- 서킷 브레이커 (Circuit Breaker) : 목적지 서비스에 문제가 있을 경우, 해당 서비스로 통신하는 것을 차단하는 기능
- 폴트 인젝션 (Fault Injection) : 의도적으로 요청 지연 및 실패 구현
- 속도 제한 (Rate Limit) : 요청 개수 제한
1.1. Istio
서비스 메시 구현체의 대표적인 도구로는 이스티오 (Istio), 링커드 (Linkerd) 가 있습니다.
이번 스터디는 이스티오를 사용하여 서비스 메시를 공부했습니다.
이스티오는 앞서 말한 Proxy 통신의 중앙 관리 도구로써 동작하며 크게 Pilot, Citadel, Galley 의 3 요소로 구성되어 있습니다.
현재는 이 3가지 요소가 'istiod' 라는 컴포넌트 안에 다 포함되어 있습니다.
* 파일럿 (Pilot)
- 모든 Envoy 사이드카 프록시 라우팅 규칙 관리
- 서비스 디스커버리와 로드밸런싱 설정 제공
* 갤리 (Galley)
- 이스티오와 쿠버네티스를 연결해주는 역할
- 서비스 메시 구성 데이터를 검증하고 변환
* 시타델 (Citadel)
- 보안 기능 담당
- TLS 인증서 발급 및 관리, 서비스 간 통신 암호화 수행
이스티오의 전체 동작 방식은 아래 아키텍처와 같습니다.
쿠버네티스에 이스티오를 구성하게 되면 파드에 사이드카로 엔보이 프록시가 들어가있는 형태가 되며,
엔보이 프록시가 들어간 파드 간 통신은 모두 이스티오를 통해 메트릭이 수집되고 트래픽을 컨트롤 할 수 있습니다.
1.2. Envoy
Envoy 는 L4/L7 프록시로 CNCF 에서 쿠버네티스, 프로메테우스에 이어 3번째로 졸업한 프로젝트인만큼 역사가 오래되고 검증된 도구라고 할 수 있습니다.
Envoy 용어
- Cluster : 엔보이가 트래픽을 포워드할 수 있는 논리적인 서비스, 실제 요청이 처리되는 IP 또는 엔드포인트 묶음
- Endpoint : IP 주소, 네트워크 노드로 그룹핑. 실제 접근이 가능한 엔드포인트를 의미
- Listener : 무엇을 받을 지, 어떻게 처리할 지에 대한 IP/Port 바인딩. 다운스트림 요청 처리
- Route : Listener 로 들어온 요청을 어디로 라우팅할 것인지 정의
- Filter : Listener 로부터 서비스에 트래픽을 전달하기 까지의 요청 처리 파이프라인
- Upstream : 엔보이 요청을 포워딩해서 연결하는 백엔드 네트워크 노드
- Downstream : 원격 클라이언트 요청
2. Envoy 프록시 실습
2.1. Envoy 프록시 실습 환경 구성
이번 서비스 메시 실습은 AWS EC2 를 생성하여 진행했습니다.
# CloudFormation 스택 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-7w.yaml
STACK_NAME=kimalarm-stack
AWS_REGION=ap-northeast-2
KEY_NAME=kimalarmkey
aws cloudformation deploy \
--template-file kans-7w.yaml \
--stack-name ${STACK_NAME} \
--parameter-overrides KeyName=${KEY_NAME} SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 \
--region ${AWS_REGION}
CloudFormation 으로 생성한 EC2 중 testpc 에 설치
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
2.2. Envoy 데모 Config 구성
testpc 에 접근 후 구성 - Terminal 1번
해당 데모 Config 구성 파일은 아래와 같으며, 공식 문서에서 발췌했습니다.
'socket_address' 에 명시된 것처럼 모든 주소 (0.0.0.0) 의 10000 번 포트로 들어오는 트래픽을,
'route_config' 에 명시된 것처럼 service_envoy_proxy_io 로 전달하고, 호스트명을 www.envoyproxy.io 로 rewrite 합니다.
service_envoy_proxy_io 에는 endpoint 로 www.envoyproxy.io:443 으로 트래픽을 처리합니다.
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
host_rewrite_literal: www.envoyproxy.io
cluster: service_envoyproxy_io
clusters:
- name: service_envoyproxy_io
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
connect_timeout: 5s
load_assignment:
cluster_name: service_envoyproxy_io
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.envoyproxy.io
curl -O https://www.envoyproxy.io/docs/envoy/latest/_downloads/92dcb9714fb6bc288d042029b34c0de4/envoy-demo.yaml
envoy -c envoy-demo.yaml
2.3. Envoy 접속 테스트
testpc 서버 - Terminal 2번
앞에서 엔보이를 실행한 터미널 외에 두 번째 터미널을 실행 시킨 후에 아래 명령어를 입력합니다.
# 엔보이 프록시 포트 확인 - 10000 번이 실제로 열려있는 지
ss -tnlp
# Envoy 트래픽 통신 확인
curl -s http://127.0.0.1:10000 | grep -o "<title>.*</title>"
외부 접속 정보 확인 - Terminal 2번
# 서버의 공인 IP 출력
echo -e "http://$(curl -s ipinfo.io/ip):10000"
# http://43.203.235.115:10000
Envoy 관리자 설정 페이지 덮어쓰기 - Terminal 1번
9902 포트로 접근 시, Envoy 관리자 페이지로 접근할 수 있도록 하는 설정을 덮어씁니다.
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)"
외부 접속 정보 확인 - Terminal 2번
# 서버의 공인 IP 출력
echo -e "http://$(curl -s ipinfo.io/ip):9902"
# http://43.203.235.115:9902
해당 페이지에서는 Envoy 의 Cluster, Listner, Server 등의 여러 정보들을 확인할 수 있습니다.
3. Istio 실습
3.1. 실습 환경 구성
k3s-s 서버에서 실행
Istio 는 현재 가장 최신 버전인 1.23.2 버전을 설치했고,
Sidecar 모드와 Ambient 모드 중 Sidecar 방식으로 진행했습니다.
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 -
# Istioctl 명령어를 사용할 수 있도록 복사
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
istioctl version --remote=false
# 디렉터리 구조 확인
tree istio-$ISTIOV -L 2 # sample yaml 포함
Istio 기본 구성
오퍼레이터를 통한 이스티오 설치는 프로파일 통해서 필요한 옵션과 설정을 다 맞춰줍니다.
다만 이스티오 1.23 버전 이후 부터는 오퍼레이터 API 가 deprecated 될 예정입니다.
실습은 편의를 위해 오퍼레이터 API 를 사용했습니다.
# Istio 프로파일 확인
istioctl profile list
# 이스티오 프로파일 목록
Istio configuration profiles:
ambient
default
demo
empty
minimal
openshift
openshift-ambient
preview
remote
stable
편의를 위한 Default 프로파일 수정
istioctl profile dump demo > demo-profile.yaml
# 아래와 같이 변경
egressGateways:
- enabled: false
# 이스티오 설치
istioctl install -f demo-profile.yaml -y
# 설치 확인 : 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 Proxy 버전
kubectl exec -it deploy/istio-ingressgateway -n istio-system -c istio-proxy -- envoy --version
# Istio IngressGateway 를 NordPort 타입으로 변경
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"type":"NodePort"}}'
# istio-ingressgateway 서비스 포트 정보 확인
kubectl get svc -n istio-system istio-ingressgateway -o jsonpath={.spec.ports[*]} | 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
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
Ingress Gateway 의 포트를 확인해보면, 각 서비스별 수신하고 있는 포트 정보를 확인할 수 있습니다.
이스티오의 중앙 관리 시스템(컨트롤 플레인)인 Istiod 를 확인해보면 내부적으로 소켓 통신을 하고 있는 것을 알 수 있으며,
앞서 보았던 Pilot 이 동작하고 있는 것을 확인할 수 있습니다.
3.2. Auto Injection 설정
네임스페이스에 특정 Label 이 있으면, 해당 네임스페이스에 파드가 배포될 때 Proxy 를 자동으로 주입하는 설정입니다.
default 네임스페이스에 'istio-injection=enabled' 라는 Label 을 주입합니다.
kubectl label namespace default istio-injection=enabled
kubectl get ns -L istio-injection
3.3. Istio 테스트를 위한 환경 구성
k3s-s 서버 - Terminal 1번
# istio ingress gw NodePort(HTTP 접속용) 변수 지정
export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
echo $IGWHTTP
# 접속을 위한 /etc/hosts 파일 수정 - 아무 도메인 가능
export MYDOMAIN=www.kimalarm.com
echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile
testpc 서버 - Terminal 2번
# k3s-s 서버에서 출력된 IGWHTTP 포트
IGWHTTP=<각자 출력된 NodePort>
IGWHTTP=32251
# 접속을 위한 /etc/hosts 파일 수정 - k3s-s 서버에서 연결한 도메인
export MYDOMAIN=www.kimalarm.com
echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile
Local PC - Terminal 3번
# k3s-s 서버에서 출력된 IGWHTTP 포트
IGWHTTP=<각자 출력된 NodePort>
IGWHTTP=32251
ISTIONODEIP=<k3s-s 의 유동 공인 IP>
ISTIONODEIP=43.202.48.135
# 접속을 위한 /etc/hosts 파일 수정 - k3s-s 서버에서 연결한 도메인
export MYDOMAIN=www.kimalarm.com
echo "$ISTIONODEIP $MYDOMAIN" | sudo tee -a /etc/hosts
3.4. Istio 외부 노출
앞서 3곳에서 Istio 접속을 위한 구성을 했습니다.
이제 실제 외부에서 Istio 접속을 시도하기 위한 Nginx 파드를 배포하겠습니다.
Nginx Deployment 생성
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
kubectl get pod -o wide
# Pod 확인
kc describe pod
... 생략 ...
# Init Container 를 통해 파드 실행 시, Istio iptable 을 설정해줌
Init Containers:
istio-init:
Container ID: containerd://d5120b8cb7d36a1f78522becaa98754dc51a6071e0a5f0200050cca716a4a5fe
Image: docker.io/istio/proxyv2:1.23.2
Image ID: docker.io/istio/proxyv2@sha256:2876cfc2fdf47e4b9665390ccc9ccf2bf913b71379325b8438135c9f35578e1a
Port: <none>
Host Port: <none>
Args:
istio-iptables
-p
15001
-z
15006
-u
1337
-m
REDIRECT
-i
*
-x
-b
*
-d
15090,15021,15020
--log_output_level=default:info
... 생략 ...
# 자동으로 Istio-Proxy 파드가 주입되어 실행됨 (Auto-Injection)
Containers:
deploy-websrv:
Container ID: containerd://ff5122cc32f6aebdb4f35936e31c9b881d0c03310a858d4ea01466a6e9c52f20
Image: nginx:alpine
istio-proxy:
Container ID: containerd://788948849ab175649d650e440ff269a427c416e3ef77ba40a7f956a5d5666623
Image: docker.io/istio/proxyv2:1.23.2
Image ID: docker.io/istio/proxyv2@sha256:2876cfc2fdf47e4b9665390ccc9ccf2bf913b71379325b8438135c9f35578e1a
Port: 15090/TCP
Host Port: 0/TCP
Args:
proxy
sidecar
--domain
$(POD_NAMESPACE).svc.cluster.local
--proxyLogLevel=warning
--proxyComponentLogLevel=misc:error
--log_output_level=default:info
Gateway 와 VirtualService 배포
이스티오의 트래픽을 처리해줄 Gateway 와 VirtualService 리소스를 배포합니다.
각 리소스는 다음과 같은 역할을 합니다.
* Gateway
지정한 인그레스 게이트웨이로부터 트래픽이 인입, 프로토콜 및 포트, HOSTS, Proxy 등 설정 가능
* VirtualService
인입 처리할 hosts 설정, L7 PATH 별 라우팅, 목적지에 대한 정책 설정 가능 (envoy route config)
k3s-s 서버에서 실행
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
istioctl proxy-status
프록시 상태 용어
- CDS(Cluster Discovery Service) : 클러스터 상태
- LDS(Listener Discovery Servic) : 리스너 상태
- EDS(ndpoint Discovery) : 엔드포인트 상태
- RDS(Route Discovery Service) : 라우트 상태
외부 PC 에서 접속 테스트
1. testpc 서버 - Terminal 2번
2. Local PC - Terminal 3번
curl -v -s $MYDOMAIN:$IGWHTTP
Istio 프록시 정보 확인
아래 명령어를 입력하면, Nginx 파드의 사이드카로 주입된 이스티오 프록시가 어떤 통신을 하고 있는 지 알 수 있습니다.
# envoy 설정 정보 확인
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
(⎈|default:N/A) root@k3s-s:~# kubectl exec -it deploy/deploy-websrv -c istio-proxy -- ss -nlp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
nl UNCONN 0 0 0:0 *
nl UNCONN 896 0 4:0 *
nl UNCONN 4352 0 4:40 *
nl UNCONN 0 0 9:0 *
nl UNCONN 0 0 10:0 *
nl UNCONN 0 0 12:0 *
nl UNCONN 0 0 15:0 *
nl UNCONN 0 0 16:0 *
u_str LISTEN 0 4096 etc/istio/proxy/XDS 99549 * 0 users:(("pilot-agent",pid=1,fd=10))
u_str LISTEN 0 4096 var/run/secrets/workload-spiffe-uds/socket 99548 * 0 users:(("pilot-agent",pid=1,fd=8))
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:15004 0.0.0.0:* users:(("pilot-agent",pid=1,fd=12))
tcp LISTEN 0 4096 127.0.0.1:15000 0.0.0.0:* users:(("envoy",pid=13,fd=18))
tcp LISTEN 0 4096 0.0.0.0:15090 0.0.0.0:* users:(("envoy",pid=13,fd=21))
tcp LISTEN 0 4096 0.0.0.0:15090 0.0.0.0:* users:(("envoy",pid=13,fd=20))
tcp LISTEN 0 4096 0.0.0.0:15001 0.0.0.0:* users:(("envoy",pid=13,fd=35))
tcp LISTEN 0 4096 0.0.0.0:15001 0.0.0.0:* users:(("envoy",pid=13,fd=34))
tcp LISTEN 0 4096 0.0.0.0:15006 0.0.0.0:* users:(("envoy",pid=13,fd=37))
tcp LISTEN 0 4096 0.0.0.0:15006 0.0.0.0:* users:(("envoy",pid=13,fd=36))
tcp LISTEN 0 4096 0.0.0.0:15021 0.0.0.0:* users:(("envoy",pid=13,fd=23))
tcp LISTEN 0 4096 0.0.0.0:15021 0.0.0.0:* users:(("envoy",pid=13,fd=22))
tcp LISTEN 0 511 [::]:80 [::]:*
tcp LISTEN 0 4096 *:15020 *:* users:(("pilot-agent",pid=1,fd=3))
해당 명령어 입력 시 위와 같은 정보가 출력되며, 이스티오가 어떤 포트를 사용하는 지는 공식 문서에 잘 나와 있습니다.
Istio-Proxy
Istiod (컨트롤 플레인)
3.5. Istio 디버깅용 파드 생성
Istio-Proxy 컨테이너에 들어가 쉘 명령어를 입력할 경우 제한되는 부분이 종종 있습니다.
이러한 점을 방지하기 위해 해당 파드를 복제하여 디버깅용 파드를 생성할 수 있습니다.
kubectl debug $(kubectl get pod -l app=deploy-websrv -oname) -it --image=nicolaka/netshoot -c netdebug --share-processes --copy-to=websrv-debug --profile='sysadmin'
netshoot 파드가 주입되어 총 3개의 컨테이너가 확인됩니다.
프로세스를 전부 복제했기 때문에, Nginx 또한 접속이 가능합니다.
이러한 방법을 통해 이스티오 뿐만 아니라 다른 보안 내용이 적용된 파드도 디버깅이 가능합니다.
이번 글에서는 Istio 와 Envoy 의 간략한 설명과 구성에 대해서 알아보았습니다.
다음 글에서는 실제 데모 어플리케이션을 배포하고 다양한 Istio 의 기능에 대해서 알아보겠습니다.
'Kubernetes' 카테고리의 다른 글
[Study 8주차] 쿠버네티스 Cilium CNI 개요 (3) | 2024.10.26 |
---|---|
[Study 7주차] 쿠버네티스 Service Mesh - Istio 기능 & Bookinfo 실습 (4) | 2024.10.19 |
[Study 6주차] 쿠버네티스 Gateway API (3) | 2024.10.13 |
[Study 6주차] 쿠버네티스 Ingress (4) | 2024.10.13 |
[Study 5주차] 쿠버네티스 Service - IPVS Proxy 모드 (2) | 2024.10.02 |