kans3) istio egressgateway 체험기
service mesh
마이크로서비스가 나오면서 내부 간의 트래픽 관리나 보안성을 강화하기 위해 서비스메시가 등장하게되었다. 🔗 그 중 하나인 istio에 대해 공부했다. 어플리케이션 간의 트래픽을 제어하려고 각 파드에 사이드카로 proxy가 함께 떠있다. 파드에 들어오는 트래픽을 이 proxy가 가로채고 관리하는 것이다. 이걸 하기 위해서 파드 안에 iptables를 쓰게되는데.. 한번 들어오는 패킷은 정신없게 Iptables를 타고 나가게되는거다..
이미지 출처 🔗
envoy
istio에서 프록시로 사용되는 Envoy proxy는 애플리케이션 간 네트워크가 투명하게 보여야 하고, 문제가 생기면 그 원인을 쉽게 파악할 수 있어야 한다는 철학에서 시작되었다고 문서에 나와있다. 🔗
구성요소
- upstream: envoy가 요청을 전달하는 서버 또는 서비스
- downstream: envoy에 연결하는 클라이언트 또는 애플리케이션
- listener: 특정 IP와 포트에서 요청을 받아들이는 역할
- filter: listener와 cluster 연결, 요청을 처리하는 파이프라인 모듈
- cluster: endpoint들의 집합으로, envoy가 요청 할 수 있는 논리적 서비스
- endpoint: 실제 접근 가능한 endpoint ip. 네트워크 노드
그림에서 보면 왼쪽에서 listener로 들어와서 정의된 네트워크 필터에 의해 클러스터로 전달되고, 클러스터는 로드밸런싱으로 알맞은 엔드포인드로 트래픽을 전달하게 된다.
istio
envoy proxy에서 모든 네트워크 트래픽을 가로채서 Istio에서 설정한 구성에 따라 패킷이 전달 될 수 있도록 한다. 구성을 크게 살펴보면, controlplane에서 프록시를 구성하고, dataplane에는 실제 동작 하는 부분으로 사이드카로 배포되어있는 envoy proxy가 있다.
구성요소
controlplane에 있는 istod 데몬 파드 하나에 여러 프로세스가 떠있다.
- galley: 구성 관리, 수집, 배포를 담당. 클러스터 내의 정보를 수집한 것을 pilot에 전달한다.
- pilot: 각 서비스에 대한 라우팅 규칙을 설정하고, 이를 proxy에게 배포한다.
- citadel: 보안 관련 요소로, 연결 시 mTLS를 사용하여 암호화, 인증서 관리 등을 한다.
실습환경
# 스터디에서 제공해주는 스택파일로 올렸다.
❯ curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-7w.yaml
❯ aws cloudformation deploy --template-file kans-7w.yaml --stack-name mylab --parameter-overrides KeyName=nyoung SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2
❯ 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
testpc 13.125.214.128 running
k3s-s 54.180.162.246 running
k3s-w2 3.38.104.74 running
k3s-w1 3.35.53.185 running
❯ ssh -i ./nyoung.pem ubuntu@54.180.162.246
(⎈|default:N/A) root@k3s-s:~# k get no
NAME STATUS ROLES AGE VERSION
k3s-s Ready control-plane,master 3h10m v1.30.5+k3s1
k3s-w1 Ready <none> 3h10m v1.30.5+k3s1
k3s-w2 Ready <none> 3h10m v1.30.5+k3s1
istio 설치
istio 설치할 때, profile을 선택해서 설치 할 수가 있다. 어떤 구성요소르 설치할건지에따라 선택할 수 있다. 아래 실습에서는 demo profile을 선택해서 설치했다. 🔗
# istioctl 설치
(⎈|default:N/A) root@k3s-s:~# export ISTIOV=1.23.2
(⎈|default:N/A) root@k3s-s:~# echo "export ISTIOV=1.23.2" >> /etc/profile
(⎈|default:N/A) root@k3s-s:~# curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV TARGET_ARCH=x86_64 sh -
(⎈|default:N/A) root@k3s-s:~# tree istio-$ISTIOV -L 2
(⎈|default:N/A) root@k3s-s:~# cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
(⎈|default:N/A) root@k3s-s:~# istioctl version --remote=false
(⎈|default:N/A) root@k3s-s:~# istioctl profile dump --config-path components.ingressGateways
- enabled: true
name: istio-ingressgateway
(⎈|default:N/A) root@k3s-s:~# istioctl profile dump --config-path values.gateways.istio-ingressgateway
{}
(⎈|default:N/A) root@k3s-s:~# istioctl profile dump demo
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
components:
base:
enabled: true
# egressgateway와 ingressgateway 활성화
egressGateways:
- enabled: true
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
(⎈|default:N/A) root@k3s-s:~# istioctl profile dump demo > demo-profile.yaml
(⎈|default:N/A) root@k3s-s:~# istioctl install -f demo-profile.yaml -y
|\
| \
| \
| \
/|| \
/ || \
/ || \
/ || \
/ || \
/ || \
/______||__________\
____________________
\__ _____/
\_____/
✔ Istio core installed ⛵️
✔ Istiod installed 🧠
...
# 기본으로 생성되어있는 pod,svc
(⎈|default:N/A) root@k3s-s:~# k get all -n istio-system
NAME READY STATUS RESTARTS AGE
pod/istio-egressgateway-57b6df4bcd-lpsdv 1/1 Running 0 72s
pod/istio-ingressgateway-5f9f654d46-pswq8 1/1 Running 0 72s
pod/istiod-7f8b586864-zrhkw 1/1 Running 0 80s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/istio-egressgateway ClusterIP 10.10.200.109 <none> 80/TCP,443/TCP 72s
service/istio-ingressgateway LoadBalancer 10.10.200.31 192.168.10.10,192.168.10.101,192.168.10.102 15021:31535/TCP,80:30465/TCP,443:31462/TCP,31400:31633/TCP,15443:30813/TCP 72s
service/istiod ClusterIP 10.10.200.121 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 80s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/istio-egressgateway 1/1 1 1 72s
deployment.apps/istio-ingressgateway 1/1 1 1 72s
deployment.apps/istiod 1/1 1 1 80s
NAME DESIRED CURRENT READY AGE
replicaset.apps/istio-egressgateway-57b6df4bcd 1 1 1 72s
replicaset.apps/istio-ingressgateway-5f9f654d46 1 1 1 72s
replicaset.apps/istiod-7f8b586864 1 1 1 80s
# 눈으로 보기 좋게 kiali를 설치
(⎈|default:N/A) root@k3s-s:~# kubectl apply -f ~/istio-1.23.2/samples/addons
(⎈|default:N/A) root@k3s-s:~# kubectl rollout status deployment/kiali -n istio-system
(⎈|default:N/A) root@k3s-s:~# kubectl patch svc -n istio-system kiali -p '{"spec":{"type":"NodePort"}}'
(⎈|default:N/A) root@k3s-s:~# KIALINodePort=$(kubectl get svc -n istio-system kiali -o jsonpath={.spec.ports[0].nodePort})
(⎈|default:N/A) root@k3s-s:~# echo -e "KIALI UI URL = http://$(curl -s ipinfo.io/ip):$KIALINodePort"
KIALI UI URL = http://54.180.162.246:31034
웹콘솔에 들어가서 트래픽 그래프에 가면, 아무런 통신이 없었기 때문에 빈페이지를 확일 할 수 있음
# envoy container가 생성될 수 있게 namespace에 라벨을 달아줬다.
(⎈|default:N/A) root@k3s-s:~# kubectl label namespace default istio-injection=enabled
namespace/default labeled
(⎈|default:N/A) root@k3s-s:~# kubectl get ns -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 3h51m enabled
istio-system Active 8m38s
kube-node-lease Active 3h51m
kube-public Active 3h51m
kube-system Active 3h51m
# netshoot이미지로 만든 테스트 파드를 보면, 2/2로 컨테이너가 두개인 것으로 확인
(⎈|default:N/A) root@k3s-s:~# k describe pod pod1
...
# init container가 뜨면서 iptables를 설정
Init Containers:
istio-init:
...
Image: docker.io/istio/proxyv2:1.23.2
Args:
istio-iptables
-p
15001
-z
15006
-u
1337
-m
REDIRECT
-i
*
-x
-b
*
-d
15090,15021,15020
--log_output_level=default:info
Containers:
# 생성 시, 지정했던 이미지
pod1:
Image: nicolaka/netshoot
...
# envoy proxy가 올라오는 사이드카 컨테이너
istio-proxy:
Image: docker.io/istio/proxyv2:1.23.2
Args:
proxy
sidecar
--domain
$(POD_NAMESPACE).svc.cluster.local
--proxyLogLevel=warning
--proxyComponentLogLevel=misc:error
--log_output_level=default:info
# 파드 이벤트 보면, init container > nginx container > istio-proxy container 순으로 올라왔다.
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 60s default-scheduler Successfully assigned default/deploy-websrv-7cdd458999-pxgll to k3s-w1
Normal Pulled 60s kubelet Container image "docker.io/istio/proxyv2:1.23.2" already present on machine
Normal Created 60s kubelet Created container istio-init
Normal Started 60s kubelet Started container istio-init
Normal Pulling 59s kubelet Pulling image "nicolaka/netshoot"
Normal Pulled 55s kubelet Successfully pulled image "nicolaka/netshoot" in 4.921s (4.921s including waiting). Image size: 20506631 bytes.
Normal Created 55s kubelet Created container pod1
Normal Started 54s kubelet Started container pod1
Normal Pulled 54s kubelet Container image "docker.io/istio/proxyv2:1.23.2" already present on machine
Normal Created 54s kubelet Created container istio-proxy
Normal Started 54s kubelet Started container istio-proxy
# node가 public ip를 가지고 있기 떄문에, 외부 호출은 잘된다.
(⎈|default:N/A) root@k3s-s:~# k exec pod1 -it -- curl ifconfig.me
54.180.162.246
egress gateway
istio api로는 한번 실패했다가.. gateway api로 다시 설치하고 성공했다. 이게 정신이 온전할 때 해봐야하는데, 자꾸 새벽에 숙제를 해서 그런지..😇🥲 참고한 문서는 이거… 🔗
# mesh 밖에 있는 엔드포인트에 대한 정보를 담고 있는 service entry 생성
(⎈|default:N/A) root@k3s-s:~# cat serviceentry.yaml
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: testserviceentry
namespace: istio-system
spec:
hosts:
- ifconfig.me
ports:
- number: 80
name: http-port
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
resolution: DNS
(⎈|default:N/A) root@k3s-s:~# k apply -f serviceentry.yaml
serviceentry.networking.istio.io/testserviceentry created
# kiali 화면에서 보면, pod1 > passthroughCluster로 나온다.
(⎈|default:N/A) root@k3s-s:~# k exec pod1 -- curl -sSL -o /dev/null -D - ifconfig.me
HTTP/1.1 200 OK
date: Sun, 20 Oct 2024 07:38:21 GMT
content-type: text/plain
content-length: 12
access-control-allow-origin: *
via: 1.1 google
x-envoy-upstream-service-time: 184
server: envoy
# gatewayAPI를 사용해서 crd 설치
(⎈|default:N/A) root@k3s-s:~# kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created
# gatewayclass 중에 istio 선택해서 gateway를 배포했다.
(⎈|default:N/A) root@k3s-s:~# k get gatewayclass
NAME CONTROLLER ACCEPTED AGE
istio istio.io/gateway-controller True 63m
istio-remote istio.io/unmanaged-gateway True 63m
# gatewayAPI 배포
(⎈|default:N/A) root@k3s-s:~# cat gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: egress-gateway
namespace: istio-system
annotations:
networking.istio.io/service-type: ClusterIP
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: ifconfig.me
port: 80
protocol: HTTP
allowedRoutes:
# 다른 namespace에서 올 수 있게 설정
namespaces:
from: All
(⎈|default:N/A) root@k3s-s:~# k apply -f gateway.yaml
gateway.gateway.networking.k8s.io/egress-gateway created
# httpRoute 설정
(⎈|default:N/A) root@k3s-s:~# cat route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: direct-to-egress-gateway
namespace: istio-system
spec:
parentRefs:
- kind: ServiceEntry
group: networking.istio.io
name: testserviceentry
namespace: istio-system
rules:
- backendRefs:
- name: egress-gateway-istio
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: forward-from-egress-gateway
namespace: istio-system
spec:
parentRefs:
- name: egress-gateway
namespace: istio-system
hostnames:
- ifconfig.me
rules:
- backendRefs:
- kind: Hostname
group: networking.istio.io
name: ifconfig.me
port: 80
(⎈|default:N/A) root@k3s-s:~# k apply -f route.yaml
httproute.gateway.networking.k8s.io/direct-to-egress-gateway created
httproute.gateway.networking.k8s.io/forward-from-egress-gateway created
(⎈|default:N/A) root@k3s-s:~# k exec pod1 -- curl -sSL -o /dev/null -D - ifconfig.me
HTTP/1.1 200 OK
date: Sun, 20 Oct 2024 08:19:49 GMT
content-type: text/plain
content-length: 12
access-control-allow-origin: *
via: 1.1 google
x-envoy-upstream-service-time: 158
server: envoy