7W - 이스티오 메시 스케일링
개요
원래 7주차에 진행한 내용이고, 당시 내용을 올리기는 했었으나 정리가 거의 되어 있지 않아 PUBLISHED로 글을 옮기지 않았다.
마지막 스터디가 끝나고 여유가 생겨 뒤늦게나마 내용을 정리하여 올린다.
아예 순서를 주차에 맞춰 바꿀까 싶기도 하지만, 주차 별 순서에 얽매이는 내용은 아니라 이 순서를 당장은 유지한다.
이번 노트에서는 이스티오 메시를 확장하는 전략에 대해 알아본다.
서비스 메시를 어떻게 구성할 것인가에 대해 이스티오는 다양한 모델을 제공하고 있다.
컨트롤 플레인을 여러 개 둔다던가, 여러 메시를 연합한다던가, 여러 클러스터가 결합된 하나의 메시를 구성한다던가..
언뜻 보면 이미 서비스 메시라는 것이 MSA 환경에서 분산된 환경을 하나로 잘 관리하기 위해 있는 건데 왜 굳이 또 그럼 메시들을 합치느니 하는 복잡한 전략을 구상하는 건가 싶을 수 있다.
복수 모델의 장점
이스티오를 하나의 메시로만 가정하고 구성하는 것도 충분히 훌륭한 방법이다.
애초에 하나의 클러스터, 하나의 네트워크, 하나의 컨플이 하나의 메시 속에 들어있는 것이 기본적으로 이스티오를 활용하는 방식이다.
이미 이스티오 자체가 MSA 환경에서 다양한 서비스를 분리하고 관리할 수 있도록 도와주는 도구이기에 그렇다.
HA를 한다고 해도 한 클러스터 내에서 전부 해결할 수 있는 문제니까 말이다.
그럼에도 여러 클러스터, 여러 메시를 두는 것은 다양한 이점을 제공할 수 있다.
- 분절되는 서비스, 팀 간 격리성 강화
- 클러스터 전반에 영향을 끼치는 설정과 운영이 장애 경계 설정
- 조직 보안, 감사 차원의 컴플라이언스
- 지역 별 배치를 통한 가용성 및 성능 향상
- 다중 및 하이브리드 클라우드
컴플라이언스야 그냥 규정 자체이니 제외하고, 사실 다른 것들은 서비스 목표를 어떻게 세웠는가에 따라서 하나의 이스티오를 운영한다고 해도 달성할 수 있는 사항이다.
그럼에도 운영조직에 따라서는 한 메시 환경에서 제공할 수 있는 그 이상의 분절과 격리성을 위하여 이러한 선택지를 충분히 고려할 수 있으며, 이스티오 역시 그러한 다양한 전략이 가능하도록 기능을 제공하고 있다.
메시를 멀티로 가져가는 것은 운영 조직의 마음이긴 할 텐데, 구체적으로 어떨 때 멀티로 세팅하게 될까?
개인적으로 멀티 메시가 구성되는 상황을 3 가지 정도 생각해봤다.
- 처음부터 서비스와 관리 주체가 명확하게 나뉘는 상황
- 대기업에서 다양한 사업을 추진하며 서비스를 운영할 때
- 서비스 간 상호작용이 필요한 순간은 있으나 각 서비스의 기능이 확실하게 분리될 때
- 이를 같은 메시 환경에서 운영하며 발생할 수 있는 시끄러운 이웃 문제나 장애 확산의 리스크를 질 이유가 없다.
- 어차피 관리팀이 명확히 분리되는 상황이라면 사실 이를 같은 메시 환경으로 관리하는 게 더 어색하지 않은가?
- 조직 간 결합
- 사업체 간 협업을 하면서 네트워크 망을 연결하게 되는 상황
- 조직 분리
- 점차 서비스가 확장되어가며 서비스의 정의가 세분화되는 상황
- 초기에는 같은 메시로 구성되는 게 이상적이었으나 규모가 커지며 점차 팀을 분리해 관리하는 것이 더 이득일 때
이러니 저러니 하지만, 결국 관리팀이 명확하게 구분되는 상황일 때 멀티 메시를 고려해봄직하지 않을까 하는 생각이다.
거시적인 관점에서는 멀티 메시라고는 하나 사실 대체로 복수의 메시를 갖추게 되는 운영 플로우는 조직적으로 이미 분절된 상황에서 각각 환경을 구축한 이후에 이들을 통합 구성하는 방향이 아닐까 싶다.
멀티 클러스터 메시
이스티오에서는 구체적으로 다음의 개념들을 단일화시킬지, 복수로 세팅할지를 차원으로 삼아 배포 모델을 분기한다.
- 클러스터 - 쿠버네티스 클러스터를 의미한다.
- 네트워크 - 라우팅이 필요없는 단위를 말한다.
- 컨트롤 플레인 - istiod를 말한다.
- 메시 - 이게 가장 헷갈리는 포인트일 수 있는데, 이건 사실 그냥 논리적 단위다.
이들이 각각의 고유한 모델을 가지고 있다는 것이 아니라 각 기준을 체크리스트로서 네트워크 모델을 분류할 수 있다는 것으로 이해하면 되겠다.
구체적으로 어떤 식으로 배포 모델을 분류할 수 있는지 알아보자.
전반적인 요약해서 가능한 조합을 따지자면 이 정도가 되는 것 같다.
그러나 각 모델을 전부 다 다루지는 않고, 멀티 클러스터 메시를 조금 중점적으로 파볼 것이다.
멀티 클러스터 메시를 구성할 때 고려사항, 구현 방법에 대해 이스티오에서 제공해주는 기능을 실습하기 위해서다.
다양한 배포 전략에 대해 심화된 내용은 이스티오 배포 전략 참고.
멀티 클러스터
복수의 클러스터라고 하면 위와 같이 구조를 그릴 수 있다.
기본적으로 각 쿠버네티스 클러스터가 내부 통신에 주력하여 지역성을 통한 성능 향상과 고가용성을 챙기는 케이스이다.
이때 모든 클러스터는 네임스페이스 동일성(namespace sameness) 을 가진다.
즉, 클러스터 a에 default 네임스페이스에 위치한 서비스 b는 메시 환경 내에서 다른 클러스터 default 네임스페이스의 서비스 b와 네트워크 상에서 동일하며 트래픽 역시 다른 클러스터로 전달될 수 있어야 한다.
그래서 중요한 것이 바로 DNS인데, 각 쿠버네티스는 멀티 클러스터를 인지하고 DNS 리졸빙을 수행할 수 없다.
이스티오에선 호스트 이름과 ip를 매핑하기 위해 쿠버 서비스나 이스티오 서비스엔트리를 활용하기에 클러스터 간 통신을 가능하게 하기 위해서는 다음과 같은 방식을 활용할 수 있다.
- 서비스
- 각 클러스터가 스텁 서비스(stub service)를 만들어 해당 호스트 이름을 Core DNS에 등록해야 함.
- 호스트 이름만 대리해주는 서비스로, 호스트 이름 등록에만 사용되고 실제 ip는 이스티오에서 제공
- 서비스엔트리
멀티 네트워크
근데 리졸빙이고 자시고, 일단 다른 네트워크면 통신부터 가능해야 한다.
그렇다면 그렇지 않은 환경, 혹은 아예 사용하는 내부 IP 대역이 같은 클러스터 간의 통합은 어떻게 해야 할까?
멀티 네트워크 환경에서는 통신을 위해 에지에 별도의 게이트웨이를 두고 통신을 하도록 구축해야 한다.
이렇게 다른 네트워크의 서비스를 잇기 위해 설정하는 게이트웨이를 흔히 East-West Gateway라고 부른다.
해당 게이트웨이는 자신이 속한 네트워크의 모든 서비스로 트래픽을 라우팅되도록 하는 역할을 수행해야 한다.
만약 같은 네트워크 대역을 써서 IP 충돌이 발생하는 두 메시를 연결해야 하는 상황이라면 반드시 이렇게 구성해야만 한다.
멀티 컨트롤 플레인
컨트롤 플레인을 어떻게 배치할 것인가도 메시를 구성하는 중요한 기준이 된다.
(여기에서의 컨트롤 플레인은 istiod, 이스티오의 컨트롤 플레인을 말하는 것이다.)
여러 클러스터에 여러 컨트롤 플레인을 두는 것도 가능하고, 하나의 컨트롤 플레인이 다른 쪽을 완벽하게 복제하는 방식도 가능할 것이다.
컨트롤 플레인에 있어서는 여러 클러스터의 결합 상황에서 중요하게 고려할 사항이 하나 더 있다.
그것은 바로 엔드포인트 디스커버리이다.
여러 컨트롤 플레인을 둘 때, 클러스터 간 엔드포인트를 찾을 수 있도록 컨트롤 플레인이 서로의 kube-apiserver에 접근할 수 있어야 한다.
(문서에서는 여러 컨트롤 플레인이 있는 상황에 대해 한정해서 말하지만, 내가 봤을 땐 그냥 여러 클러스터면 무조건 고려할 사항이다.)
기본적으로 메시 내 워크로드와 서비스의 상황을 이스티오 컨트롤 플레인은 api 서버와 통신하여 알아내기 때문에 그렇다.
달리 말하자면 각 클러스터의 컨트롤 플레인은 상대의 시큐리티#API 서버 보안을 뚫을 수 있어야 한다는 것이다!
그래서 관련한 설정을 할 필요가 있는데 이스티오에서는 관련하여 remote secret을 쉽게 만드는 기능을 제공하고 있다.
설정을 하게 되면 각 클러스터는 서로의 클러스터의 서비스들을 탐색할 수 있게 된다.
서로 탐색이 되는 상황에서 격리가 필요하다면 이스티오 버츄얼서비스나 이스티오 데스티네이션룰, 이스티오 사이드카 등의 리소스를 활용해 이들의 가시성을 조절할 수 있을 것이다.
상호 신뢰
멀티 클러스터라 했을 때 가장 기본적일 수도 있는 사안으로, 서로가 서로를 신뢰할 수 있는 수단이 필요하다는 큰 고려사항이 또 존재한다.
이스티오 시큐리티를 보면 알겠지만 각 워크로드는 스피페 양식의 고유 신원을 가진다.
각 클러스터가 결합된 하나의 메시로 성립하기 위해서는 모두 같은 신뢰 도메인을 가지고 있어야 한다.
이렇게 구조를 세팅하기 위해서는 각 클러스터가 하나의 root CA(구체적으로는 공통 신뢰 조상)를 가져야만 한다.
해당 CA를 기반으로 클러스터 별로 체인 인증서를 두고, 이를 토대로 신뢰 도메인을 구성해야 비로소 클러스터는 서로를 신뢰하고 식별자를 활용할 수 있게 된다.
기본 이스티오 구성을 하면 각 클러스터의 신뢰 도메인은 cluster.local
로 설정된다.
이렇게 각각 root CA가 세팅된 두 메시를 결합하면 이것은 멀티 클러스터 메시일까?
핵심을 봐야 돼요!
같은 도메인인 상태에서 서로 root CA 인증서를 가지고 있으면 TLS 검증 과정이 실패하기 때문에 서로를 인증할 수 없다.
멀티 클러스터 메시의 조건
위에서 간단하게 언급됐던 내용들을 종합하면 멀티 클러스터 메시 모델이 가져야 하는 조건을 3가지 정도로 축약할 수 있겠다.
클러스터 간 워크로드 디스커버리
워크로드가 컨트롤 플레인에 연결될 수 있어야 하고, 컨트롤 플레인은 모든 클러스터의 워크로드를 찾아 레지스트리를 제공할 수 있어야 한다.
즉 컨트롤 플레인은 모든 클러스터의 api 서버에 접근할 수 있어야 한다.
이때 api 서버로 접근하기 위한 권한은 서비스 어카운트 토큰을 이용해 신원을 세팅하는데, istioctl을 통해 쉽게 설정할 수 있다.
클러스터 간 워크로드 연결성(connectivity)
각 클러스터는 엔드포인트로 실제로 연결될 수 있어야 한다.
이때 멀티 네트워크 환경이라면 각 에지를 서로 이어주는 리버스 프록시 역할을 하는 이스트웨스트 게이트웨이가 필요하다.
사실 이건 특수한 기능을 넣은 이스티오 인그레스 게이트웨이이다.
또한 멀티 클러스터이기에 서로의 도메인을 위한 스텁 서비스, DNS 프록싱을 활용한 서비스 엔트리와 같은 세팅이 필요하다.
클러스터 간 공통 신뢰
이스티오의 보안 기능을 활용하기 위해서는 클러스터 간 워크로드가 서로 인증할 수 있어야 한다.
이를 위해서는 공통된 같은 Root CA를 가지고 있어야 한다.
구체적으로는 크게 두 가지 방법을 사용할 수 있다.
- 각 클러스터에서 사용할 신뢰 조상이 같은, 중간 인증서를 만들어 이스티오 세팅할 때 넣어준다.
- Cert Manager와 같은 CSR에 서명할 수 있는 외부 기관을 활용한다.[2]
- 이 경우 컨트롤 플레인은 직접 서명하지 않고 외부 기관에 서명 요청을 하는 역할만 수행한다.
- SPIRE를 활용하는 것도 가능한 듯.
실습 전략
이제 기본적인 내용은 알아봤고, 멀티 클러스터 메시 조건을 어떻게 충족할 수 있을지, 이스티오에서는 어떤 식으로 기능을 제공하는지 실습을 하며 알아보겠다.
먼저 실습에서 세팅할 전체 서비스 플로우는 아래와 같다.
외부의 클라이언트로 mypc 컨테이너를 띄운다.
두 개의 클러스터가 있고, 각각 이스티오 메시가 구성돼있으며 이들은 서로 다른 네트워크로 상정할 것이다.
사용자의 요청은 west 클러스터의 webapp을 향하고, webapp은 east 클러스터의 catalog를 호출한다.
두 클러스터는 서비스 연동이 필요한 상태인 것이다.
여기까지가 기본 책의 실습 흐름인데, 나는 여기에 몇 가지 커스텀을 더 얹었다.
외부 CA
첫번째는 공통 CA의 외부화이다.
책의 예제에서는 Plugin CA라고 해서 각 이스티오를 세팅할 때 사전에 만들어둔 인증서를 istiod가 활용하도록 만든다.
이렇게 하는 이유는 메시 간 통신에서 mTLS를 하게 되는데 서로를 신뢰할 수 있도록 공통 root CA를 두기 위함이다.
각 서비스에 인증서를 발급해서 주입하는 역할은 평소와 다를 것 없이 그대로 istiod가 전담한다.
이 방식은 운영 환경에서 그렇게 추천되지는 않는다.
인증서를 직접 발급하고 관리해야 하기에 자동화가 어렵고 효율적이지 않기 때문이다.
그래서 현재 내가 구성하는 세팅은 istiod가 더 이상 인증서 관련 기능 기능을 수행하지 않는다.
istiod가 하는 역할은 각종 이스티오의 리소스를 기반으로 데이터 플레인의 트래픽 설정을 전달하는 것.
프록시가 받아야 하는 인증서는 각 클러스터의 Cert Manager가 담당한다.
이때 서트매니저는 프록시가 인증서를 요청하는 방식에 대한 api나 로직을 구현하고 있지 않다.
그렇기에 이 어댑터 역할을 하는 istio-csr이라는 중간자를 둔다.
설정하는 과정을 보면 istiod가 어떻게 CA 역할로부터 분리되는지 더 명확해질 것이다.
실제 공통 CA를 관리하는 것은 Vault이다.
볼트를 클러스터 외부에 두어도 상관은 없으나, 굳이 더 클러스터를 늘리지 않고 실습 편의를 위해 볼트는 west 클러스터에 배치한다.
처음 생각에는 서트 매니저 하나를 두고 두 클러스터가 이를 활용하도록 하려고 했다.
그러나 서트 매니저는 이름 그대로 클러스터의 인증서를 관리하는 도구일 뿐, 클러스터 간 연동 기능을 지원하지 않는다.
그래서 볼트가 실제로 공통 CA와 각 클러스터의 중간 CA 인증서를 관리하고 서트 매니저는 중간 CA를 활용하는 식으로 운용한다.
이 부분에 대해 삽질하며 탐구한 내용은 E-istio-csr 사용 실습에 정리돼있다.
외부 CA를 두기 위한 다른 방법들에 대한 간략한 검토도 담겨있다.
멀티 클러스터 통합 관측가능성(트래픽 플로우 성공, 트레이스 실패)
두번째 커스텀은 관측 가능성 세팅이다.
멀티 클러스터를 운영한다는 것은 운영의 복잡성을 감수해야 한다는 것이다.
이때 전체 시스템의 투명성을 확보하기 위해 통합적인 모니터링 환경을 구성할 필요가 있다.
그래서 메시 전반에 대해서는 키알리를 통해 멀티 클러스터 환경을 볼 수 있도록 세팅한다.
멀티 클러스터 키알리를 위해서는 두 가지 세팅을 해야 한다.
먼저 키알리는 하나의 메트릭 출처만 받기 때문에 기반이 되는 프로메테우스 단에서 메트릭을 집계해야 한다.
다양한 방법이 있겠으나, 여기에서는 프로메테우스 연합(federation) 방식을 쓴다.
두 번째로 키알리가 전반적인 메시 환경을 파악하기 위해서는 다른 클러스터의 kube-apiserver에 접근할 수 있도록 해야 한다.
각종 리소스 정보를 바탕으로 조금 더 세밀한 시각화 정보를 전달하기 위함이다.
참고로 트래픽 그래프 시각화만 목적이라면 사실 두번째 세팅이 필수적이지는 않다.
여기에 트레이스 데이터를 보기 위해 Jaeger를 세팅한다.
트레이스는 어차피 트래픽에 붙는 식별자를 통해 트래픽 간 연결성을 파악할 수 있기에, 멀티 클러스터 구축에서 필요한 사항은 수집된 데이터를 한 곳으로 모으는 것이다.
이스티오에서의 텔레메트리 세팅은 기본적으로 한 클러스터 내부에 모으는 방식으로 지원된다.
여기에서 추가적으로 해주면 되는 것은 그냥 모인 데이터들을 한 곳으로 모으는 작업일 뿐이다.
전반적으로 west 클러스터에 공통될 만한 컴포넌트들을 몰아 넣어서 구성하나, 아예 별도의 메시나 환경을 구성해서 운용하는 것도 가능하다.
미리 말하지만, 예거로 통합 트레이스 가시성을 확보하는 것은 실패했다.
처음 전략을 잘못 수립한 것이 화근이라고 생각하는데, 시간 나면 추가 도전으로 메꾸고자 한다.
세팅
이번 실습에는 두 클러스터를 배치해야 하기 때문에, 평소의 코드에 두 배는 더 써야 한다 ㅋㅋ
적당히 빼먹는 코드도 있을 예정인데, 관련 내용은 레포를 보는 것을 추천한다.
세팅에 있어서 순서대로 적용할 수 있도록 번호도 부여해두었다.
source 01-settings.sh
실행할 때는 이렇게 현재 쉘에다 바로 박아버리면 된다.
00 파일은 alias나 환경 변수를 넣어뒀기에 다른 터미널에서 똑같이 명령어를 치고 싶다면 해당 스크립트를 적용해주면 된다.
이라는 사전 경고를 했으니, 굳이 모든 코드를 첨부하지 않는다!
# east
kind create cluster --name east --image kindest/node:v1.32.2 --kubeconfig ./east-kubeconfig --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 31000 # istio-ingrssgateway HTTP
hostPort: 31000
- containerPort: 31001 # Prometheus
hostPort: 31001
- containerPort: 31002 # Grafana
hostPort: 31002
- containerPort: 31003 # Kiali
hostPort: 31003
- containerPort: 31004 # Tracing
hostPort: 31004
- containerPort: 31005 # kube-ops-view
hostPort: 31005
networking:
podSubnet: 10.20.0.0/16
serviceSubnet: 10.200.0.0/24
EOF
# external client
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity
# MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml \
--kubeconfig=./west-kubeconfig
kubectl -n metallb-system wait --for condition=available deployment controller --kubeconfig=./west-kubeconfig --timeout=60s
cat << EOF | kubectl apply --kubeconfig=./west-kubeconfig -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb-system
spec:
addresses:
- 172.18.255.101-172.18.255.120
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
spec:
ipAddressPools:
- default
EOF
대역대가 다른 두 클러스터를 배치한다.
여기에 트래픽 실습을 조금 더 편하게 하기 위해 MetalLB을 추가했다.
이번에는 실행해야 할 게 많다보니 아예 스크립트로 관리하는데, 각 리소스 생성 명령어 간 wait 명령어를 쓰니 원하는 만큼 대기할 수 있어서 좋은 것 같다.
볼트 + 서트 매니저 + istio-csr 세팅
다음으로 세팅해주는 것은 볼트이다.
볼트에서 루트 인증서와 클러스터 별 중간 인증서를 만들 것이고, 이 인증서를 기반으로 서트 매니저가 인증서를 발급해줄 것이다.[3]
이후에 Istio-CSR이 해당 인증서를 기반으로 이스티오의 워크로드들의 인증서 요청을 수행한다.
helm repo add hashicorp https://helm.releases.hashicorp.com
helm upgrade vault hashicorp/vault \
--install -n vault --create-namespace \
-f values-vault.yaml \
--kubeconfig ./west-kubeconfig
# Vault won't be ready unless you unseal it, just take enough time to wait for initial process
sleep 25
echo "Wait for the pod up"
kwest exec -n vault vault-0 -- vault operator init -key-shares=1 -key-threshold=1 \
-format=json > init-keys.json
alias vault="kubectl --kubeconfig ./west-kubeconfig -n vault exec vault-0 -- vault "
VAULT_UNSEAL_KEY=$(cat init-keys.json | jq -r ".unseal_keys_b64[]")
vault operator unseal $VAULT_UNSEAL_KEY
VAULT_ROOT_TOKEN=$(cat init-keys.json | jq -r ".root_token")
vault login $VAULT_ROOT_TOKEN
# check
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
vault write pki/root/generate/internal \
common_name=cluster.local \
ttl=87600h -format=json | jq -r '.data.certificate' > CA.crt
vault write pki/config/urls \
issuing_certificates="vault.vault.svc.cluster.local:8200/v1/pki/ca" \
crl_distribution_points="vault.vault.svc.cluster.local:8200/v1/pki/crl"
# West cluster cert, auth
vault secrets enable -path=west-cluster pki
vault secrets tune -max-lease-ttl=87600h west-cluster
WEST_CSR=$(vault write -format=json west-cluster/intermediate/generate/internal \
common_name="west-cluster" \
| jq -r '.data.csr')
vault write -format=json pki/root/sign-intermediate csr=$WEST_CSR \
format=pem ttl="43800h" \
| jq -r '.data.certificate' > west-cert.pem
cat west-cert.pem > west-chain.pem
cat CA.crt >> west-chain.pem
WEST_CHAIN=$(cat west-chain.pem)
vault write west-cluster/intermediate/set-signed certificate=$WEST_CHAIN
vault write west-cluster/roles/west-cluster-role \
allowed_domains=istio-ca \
allow_subdomains=true \
allow_any_name=true \
enforce_hostnames=false \
require_cn=false \
allowed_uri_sans="spiffe://*" \
max_ttl=72h
거창해보이지만 별 건 없다.
볼트는 처음 설치하면 봉인 상태이기에 이걸 풀어주고, 인증서와 접근 정책을 설정한다.
이때 각 클러스터 별 기본 신원으로 쓰일 인증서도 만들어버린다.
그 다음으로 해줄 것은 서트 매니저와 istio-csr.
helm repo add jetstack https://charts.jetstack.io
# east
helm install \
cert-manager jetstack/cert-manager \
--wait \
--namespace cert-manager \
--create-namespace \
--version v1.17.2 \
--set crds.enabled=true \
--kubeconfig=./east-kubeconfig
VAULT_IP=$(kubectl --kubeconfig ./west-kubeconfig get svc -n vault vault -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
cat << EOF | kubectl apply --kubeconfig=./east-kubeconfig -f -
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: cert-manager-vault-token
namespace: istio-system
stringData:
token: $VAULT_ROOT_TOKEN
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: istio-ca
namespace: istio-system
spec:
vault:
server: http://$VAULT_IP:8200
path: east-cluster/sign/east-cluster-role
auth:
tokenSecretRef:
name: cert-manager-vault-token
key: token
EOF
cat << EOF | helm upgrade cert-manager-istio-csr jetstack/cert-manager-istio-csr \
--install \
--namespace istio-system \
--wait \
--kubeconfig=./east-kubeconfig -f -
app:
server:
clusterID: east-cluster
runtimeConfiguration:
create: true
name: istio-issuer
issuer:
name: istio-ca
kind: Issuer
group: cert-manager.io
tls:
# trustDomain: "cluster.local"
certificateDNSNames:
- cert-manager-istio-csr.istio-system.svc
EOF
서트 매니저를 세팅할 때 이슈어 리소스를 만드는데, 이 이슈어는 볼트에서 사전 생성해둔 클러스터의 인증서에 기반해 인증서를 발행할 수 있다.
(편의를 위해 그냥 루트 토큰으로 접근했다..)
이슈어가 세팅돼있는 상태라면 istio-csr을 설치하는 시점에 해당 이슈어를 참조하는 것이 가능하다.
전부 완료됐는지는 파일을 뜯어보면 된다.
iwest -n istioinaction pc secret debug -ojson | jq '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' -r | base64 -d | openssl x509 -noout -text | grep spiffe
ieast -n istioinaction pc secret debug -ojson | jq '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' -r | base64 -d | openssl x509 -noout -text | grep spiffe
해당 실습을 수없이 반복하다보니 정리를 그때그때 하지 못해서 사진 정보가 많이 남지 않았다.
다만 나중에 따라하려는 사람은 스크립트가 있으니 해당 내용을 참고 바란다.
중간에 서트매니저와 볼트를 연동하다 있었던 이슈에 대한 내용을 추가로 담아둔다.
왜인지 cr을 보면 계속 요청하는 이슈어가 istio-ca이다.
values 파일에서 이걸 수정해줬는데도 계속 이 부분에 변화가 생기지 않았다.
아마 급하게 세팅을 하느라 특정 필드에서 체크해야 할 부분을 빼먹은 게 아닌가 싶긴 한데, 일단 istio-ca로 이슈어를 바꾸니 쉽게 해결됐다.
이스티오 세팅
이제야 본격적으로 이스티오를 세팅해본다.
cat << EOF | istioctl install --kubeconfig=./west-kubeconfig -f - -y
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-controlplane
namespace: istio-system
spec:
profile: default
values:
global:
# istio-csr
caAddress: cert-manager-istio-csr.istio-system.svc:443
meshID: usmesh
multiCluster:
clusterName: west-cluster
network: west-network
components:
pilot:
k8s:
env:
- name: ENABLE_CA_SERVER
value: "false"
EOF
먼저 외부 CA를 사용하기 때문에 istiod는 더 이상 CA 서버로 기능해서는 안 된다.
그래서 위의 환경 변수를 세팅하며 더불어 외부 ca의 주소를 글로벌 변수로 지정한다.
솔직히 이러한 방식은 매우 불편하다.
헬름과 오퍼레이터 두 양식이 공존하는 현 설정 방식에서, 헬름은 자유도가 너무 높아서 뭔 값을 설정할 수 있는지 알 수도 없는데 이스티오 오퍼레이터로만 설치를 관리하자니 꼭 뭔가 중요한 기능은 헬름쪽 필드 설정을 이용해야 한다.
이런 부분은 빠르게 개선됐으면 하는 바람이다.
아무튼 이외에 meshID multiCluster 등도 세팅하는 것을 볼 수 있다.
이러한 설정 필드들이 바로 기본적으로 이스티오에서 제공하는 멀티 배포를 위한 기능 중 하나이다.
이 세팅들은 이후 각 컴포넌트의 설정이나 분기 행동을 결정짓는데 사용된다.
메시 연합(mesh federation)을 할 것은 아니기 때문에 meshID는 통일시켜 주었다.
이제 기본적인 서비스를 배치한다.
kwest -n istioinaction apply -f ch12/webapp-deployment-svc.yaml
kwest -n istioinaction apply -f ch12/webapp-gw-vs.yaml
kwest -n istioinaction apply -f ch12/catalog-svc.yaml
keast -n istioinaction apply -f ch12/catalog.yaml
keast, kwest로 배치 공간을 분리하고 있다는 것을 확인하자.
여기에서 유념할 지점 중 하나는 바로 west 클러스터에 배포되는 서비스 리소스이다.
apiVersion: v1
kind: Service
metadata:
labels:
app: catalog
name: catalog
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 3000
selector:
app: catalog
west에는 실제 catalog가 없지만 이렇게 서비스를 등록해야 하는데, 이를 스텁 서비스라고 한다.
역할은 기본적인 dns 호스트를 west 클러스터에 인식시키는 것으로 실제 엔드포인트는 가지지 않는다.
istiod는 쿠버 리소스나 서비스 엔트리를 기반으로 서비스들을 식별하고 클러스터로 구성한다.
또한 클러스터 내 워크로드들은 다른 클러스터의 호스트 이름을 알 수가 없다.
그래서 일단 더미로 서비스를 세워서 dns 리졸빙이 가능하게 만들고 istiod도 클러스터에 등록하는 행위를 할 수 있게 만든다.
멀티 클러스터가 연결되면 해당 주소에 대해 엔보이는 설정을 수신받아 엔드포인트를 구성하게 될 것이다.
멀티 클러스터 구축
이제부터 위에서 말한 3가지 요건들을 충족시켜나가보자.
디스커버리
서로 다른 클러스터에서 서로의 서비스 정보를 보는 방법은 간단하다.
이스티오는 kube api 서버와 통신하여 서비스를 디스커버리하기 때문에 상대의 api 서버에 접근할 수만 있으면 된다.
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get sa istio-reader-service-account -n istio-system --kubeconfig=./$i-kubeconfig ; echo; done
이걸 쉽게 할 수 있도록 위해 이스티오에서는 관련한 서비스 어카운트 istio-reader-service-account를 미리 세팅해두고 있다.
해당 서비스어카운트는 구체적으로 무엇을 할 수 있을까?
kwest auth can-i --as system:serviceaccount:istio-system:istio-reader-service-account --list
serviceexports
라는 커스텀 리소스에 대해서는 생성 권한을 가지고 있고, 나머지 다양한 리소스에 대해 조회 권한이 있는 것을 확인할 수 있다.
한 클러스터 외부의 이스티오가 해당 클러스터의 서비스 디스커버리를 하는데 사용할 수 있는 서비스 어카운트와 이에 대한 권한 세팅이 이미 잘 돼있다는 것이다.
다시 말해,이 서비스 어카운트의 토큰을 가져와서 이스티오에게 사용하도록 설정할 수만 있다면 이스티오는 해당 클러스터를 디스커버리할 수 있게 된다.
istioctl --kubeconfig=c0.yaml create-remote-secret --name c0 \
| kubectl --kubeconfig=c1.yaml apply -f -
이스티오에서는 이걸 또 쉽게 하는 명령어를 제공하고 있다.
1.25 기준으로는 더이상 experiment가 아니라 정식으로 지원하는 기능으로 create-remote-secret
을 이용할 수 있다.
이것은 한 클러스터에서 자신의 클러스터에 요청을 할 수 있는 설정 파일을 만든다.
위의 예시처럼 사용하면 c0이란 클러스터에 c1에서 접근할 수 있도록 하는 것이다.
ieast create-remote-secret --name="east-cluster"
해당 명령어는 하나의 시크릿을 만드는데, 이때 데이터로는 kubeconfig 파일을 담고 있다.
가장 마지막에 user 부분을 보면 토큰이 들어가는 게 보이는데 이건 인증에 Authorization 헤더에 사용할 JWT 토큰을 명시하는 부분으로 서비스 어카운트 토큰이 여기에 들어가게 된다.
jwt.io에 넣어보면 조금 더 명확하게 알 수 있다.
해당 시크릿의 kubeconfig 파일을 이용하면 위에서 봤던 istio-reader-service-account의 신원으로 해당 클러스터에 접근할 수 있게 된다는 것이다!
istio는 루트 네임스페이스에서 istio/multiCluster: true
란 라벨이 붙은 시크릿을 추적하고 kubeconfig를 인식한다.
그럼 이제 실제로 적용해보자.
ieast create-remote-secret --name="east-cluster" | sed -E 's/127.0.0.1:(.*)/east-control-plane:6443/g' | kwest apply -f -
현재 나는 KIND를 이용하고 있어 서버의 주소가 제대로 출력되지 않는다.
이에 같은 도커 네트워크에서 사용할 수 있는 도메인으로 서버 주소를 수정하는 추가 작업을 넣어주었다.
kwest logs deploy/istiod -n istio-system
west의 istio가 east 클러스터를 인식하고 추적에 들어간 것을 확인할 수 있다.
west의 webapp은 이제 east의 catalog를 인식할 수 있을 것인가?
for i in listener route cluster endpoint; do echo ">> k8s cluster : west - istio-config $i <<"; iwest pc $i -n istioinaction deploy/webapp; echo; done
클러스터, 라우트, 엔드포인트 등 다양한 부분에서 카탈로그를 인식하는 것을 확인할 수 있다!
서트 매니저도 여러개, 이스티오 관련 컴포넌트도 여러 개라 헷갈리지만, 엔드포인트 대역을 보면 조금 더 확실하게 east 클러스터의 ip 주소를 가져온 것을 확인할 수 있다.
하지만 이것만으로는 아직 통신할 수 없다.
west 클러스터는 10.10~ 서비스 대역대를 가지고 있으므로 라우터 없이는 10.20.0.0/16 대역으로 트래픽을 보낼 수 없다.
여기에 해당 대역은 파드 오버레이 네트워크 대역이므로 다른 클러스터 입장에서는 해당 대역에 대한 정보만으로 트래픽을 보내는 것 자체가 굉장히 이상한 것이다.
워크로드 연결성
두 이스티오 메시는 다른 네트워크에 위치하고 있다.
그러니 이 두 네트워크를 연결할 수 있는 방법이 필요하다.
가장 쉬운 방법은 피어링을 맺고 서로의 라우터에 경로를 추가해주는 것이다.
그러나 그럴 수 없는 다양한 환경이 존재할 수 있고, 이에 대해 이스티오에서는 동서 게이트웨이 기능을 제공한다.
동서 게이트웨이는 사실 그냥 특수한 정보를 추가적으로 가지고 있는 인그레스 게이트웨이이다.
그 정보는 SNi 필드와 관련된다.
이전에 버츄얼 서비스에서 sni 관련 필드를 보면서 알았겠지만 이스티오에서 TLS sni 필드는 구체적인 클러스터 정보를 가지고 있다.
여기에는 트래픽이 라우팅될 수 있는 유사 워크로드 집합을 묶는 속성인 방향, 부분집합, 포트, FQDN 등의 정보가 전부 포함되어 있다.
그래서 이를 기반으로도 트래픽이 가야할 방향을 사실 알 수 있는데, 동서 게이트웨이는 이를 알아서 읽어들여 자신이 트래픽을 라우팅할 클러스터로 구성하는 게이트웨이인 것이다.
이거 없이 클러스터 간 연결을 시키려면, 게이트웨이를 배치하고 해당 게이트웨이에 자신의 클러스터로 들어오는 tls 트래픽을 전부 라우팅할 수 있도록 모든 서비스에 대한 버츄얼 서비스를 만들어야 할 것이다..
설정하는 방법은 매우 간단하다.
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-eastwestgateway # IstioOperator 이름은 앞 선 이스티오 설정 이름과 겹치지 않아야 한다
namespace: istio-system
spec:
profile: empty # empty 프로필은 추가 이스티오 구성 요소를 설치하지 않는다
components:
ingressGateways:
- name: istio-eastwestgateway # 게이트웨이 이름
label:
istio: eastwestgateway
app: istio-eastwestgateway
topology.istio.io/network: east-network
enabled: true
k8s:
env:
- name: ISTIO_META_ROUTER_MODE # sni-dnat 모드는 트래픽을 프록시하는 데 필요한 SNI 클러스터를 추가한다
value: "sni-dnat"
# The network to which traffic is routed
- name: ISTIO_META_REQUESTED_NETWORK_VIEW # 게이트웨이가 트래픽을 라우팅하는 네트워크
value: east-network
service:
ports:
... (생략) ...
values:
global:
meshID: usmesh # 메시, 클러스터, 네트워크 식별 정보
multiCluster:
clusterName: east-cluster
network: east-network
ISTIO_META_ROUTER_MODE
를 sni-dnat
으로 설정하면 SNI 클러스터를 자동으로 구성한다.
ISTIO_META_REQUESTED_NETWORK_VIEW
는 네트워크 트래픽이 프록시되는 곳을 정의한다.
참고로 이스티오 오퍼레이터 설정은 양식 이름에 민감하다.
이름이 다르면 병합이 되나, 같으면 완전히 덮어씌워버리기 때문에 주의가 필요하다.
아무튼 이제 확인해보면..
east 클러스터에 동서 게이트웨이가 생겼다.
해당 동서 게이트웨이는 알아서 로드밸런서로 세팅된다.
이 상태에서는 west 클러스터에서 자그마한 변화가 생긴다.
iwest pc endpoint deploy/webapp.istioinaction
catalog에 대한 엔드포인트가 이상하게 변화된 것을 관측할 수 있다.
이제 catalog에 대해서 west 클러스터의 webapp은 엔드포인트를 동서 게이트웨이의 주소로 인식한다!
동서 게이트웨이에 네트워크가 설정된 이후 해당 네트워크에 위치한 api서버에 접근이 가능하던 west istiod에서 해당 정보를 반영하여 엔드포인트 주소를 수정한 것이다.
조금 더 파보자.
iwest pc cluster deploy/webapp.istioinaction --fqdn catalog.istioinaction.svc.cluster.local -ojson | fx
클러스터 정보를 보면 어디로 가야할지 나온다.
보면 이스티오 상에서는 분명 어디로 트래픽이 라우팅돼야 하는지 정확하게 인지하고 있다.
보안
사실 위에서 세팅을 하며 각 클러스터 인증서를 뜯어보면 같은 루트로부터 사인 됐기 때문에 이 부분은 이미 충족이 됐다.
이에 대한 검증은 상호 통신이 가능한지를 보면 된다.
통신이 되는 시점에서 이미 이 둘은 서로 상호 신뢰를 할 수 있는 관계임을 뜻하기 때문이다.
반대로 신뢰가 안 됐을 때 통신이 안 되는 경우를 미리 보여주고, 다음 구절에서 통신이 성공하는 상황을 보여주고자 한다.
동서 게이트웨이를 mypc 컨테이너가 이용하게 되는 상황을 살펴보자.
de curl -s -H "Host: catalog.istioinaction.io" https://172.18.255.201:15443/items -v
mtls를 하는 포트이기 때문에 일반적인 요청은 당연히 해당 게이트웨이를 통과할 수 없다.
테스트
그렇다면 이제 워크로드 간 통신은 성공할 수 있을까?
이게 성공하면 정말 멀티 클러스터가 구축됐다고 말할 수 있을 것이다!
웹앱이 카탈로그로 통신할 것이니 웹앱을 이용하고 싶지만, 웹앱의 기본 컨테이너에 curl이 없는 관계로 debug 파드를 이용한다.
kwest exec -ti -n istioinaction debug -- nslookup catalog.istioinaction.svc.cluster.local
먼저 dns 질의를 해보면, 사실 너무 당연하게 스텁 서비스의 ip가 출력된다.
그럼 이 상태에서 실제로 해당 도메인으로 질의를 날리면 어떻게 될까?
kwest exec -ti -n istioinaction debug -- curl catalog.istioinaction.svc.cluster.local/items/1
꽤나 흥미로운 결과를 볼 수 있다!
curl을 존재하지 않는 ip로 날렸을 때 나오는 결과와 사뭇 다른 출력을 볼 수 있다.
물론 connection refused이긴 하지만..
kwest exec -ti -n istioinaction debug -c istio-proxy -- curl catalog.istioinaction.svc.cluster.local/items/1
참고로 일반 curl 요청을 보고 싶다면 istio proxy 컨테이너를 이용해 요청을 날려보면 된다.
이건 그냥 순수하게 curl이 그대로 적용된 결과이다.
왜 istio proxy 컨테이너에서의 요청은 다른 방식으로 처리되는가?
iptables를 보면 -u 1337
이란 인자가 있는데, 이건 istio proxy 컨테이너의 메인 프로세스 id를 뜻한다.
보다시피 이 인자에 설정된 프로세스는 리디렉션의 영향을 받지 않는다.[4]
그렇기 때문에 istio proxy를 통한 curl 요청은 리디렉션되지 않아 엔보이를 거치지 않고 순수한 형태로 요청이 실패하는 것이다!
이런 문제가 발생하는 이유는 간단하다.
웹앱에서는 분명 제대로 트래픽을 보냈다.
그러나 동서 게이트웨이는 해당 트래픽을 받을 준비가 되어있지 않다.
이건 여태까지 수두룩하게 설정했던, 게이트웨이 리소스를 통해 게이트웨이의 리스너를 설정하지 않았기 때문에 발생하는 문제라는 것이다.
이제 필요한 작업은 동서 게이트웨이가 해당 호스트를 명시적으로 잡을 수 있도록 하는 것이다.
그리고 이 방법은 사실 여태 많이 봐왔다.
동서 게이트웨이 워크로드에 대해 이스티오 게이트웨이 리소스로 호스트 이름을 명시하면 게이트웨이는 해당 호스트를 리스너 설정에 반영한다.
여기에 버츄얼 서비스로 게이트웨이에 라우터 설정을 넣어주면 트래픽이 제대로 라우팅될 수 있을 것이다.
그런데... 메시 간 연결을 하고 나서 이 작업을 전부 일일히 해줄 것인가?
각 메시의 서비스가 연결될 수 있게 다시 버츄얼 서비스를 만들고 수정을 가해야 하는가?
SNI Auto Passthrough
여기에서 활용할 수 있는 기능이 오토 패스스루이다.[5]
기본적인 패스스루 방식에서는 암호 통신을 받았을 때 SNI 이름을 보고 이에 맞춰 라우팅을 진행해줬다.
이때 라우팅 설정 자체는 버츄얼 서비스를 통해서 진행했어야만 했다.
근데 오토 패스스루는 버츄얼 서비스를 만들 필요도 없이 게이트웨이가 트래픽을 받았을 때 SNI 필드의 정보를 토대로 알아서 트래픽이 가야할 곳을 정한다.
기존의 방식은 게이트웨이에게 규칙과 목적지를 알려주고 나서 게이트웨이가 동작을 했다면 이 방식에서는 게이트웨이가 트래픽을 보고 알아서 목적지를 판단해버린다는 것이다.
이 설정은 흔히 다른 네트워크 간 연결 시 활용된다고 문서에도 버젓이 나와있다.
SNI DNAT가 설정된 게이트웨이에서 활용할 수 있다.
이런 식으로 게이트웨이 리소스를 만들어준다.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: cross-network-gateway
namespace: istio-system
spec:
selector:
istio: eastwestgateway
servers:
- port:
number: 15443 # 이스티오에서 15443 포트는 멀티 클러스터 상호 TLS 트래픽 용도로 지정된 특수 포트다
name: tls
protocol: TLS
tls:
mode: AUTO_PASSTHROUGH
hosts:
- "*.local"
15443 포트는 멀티 클러스터간 mtls를 위해 지정된 특수포트로, 도메인이 local로 끝나는, 즉 쿠버의 모든 서비스 통신에 대해 오토 패스스루 설정을 넣는다.
keast apply -n istio-system -f ch12/gateways/expose-services.yaml
이 설정으로 인한 변화는 게이트웨이의 리스너 설정 부분의 변화를 보면 된다.
기존에는 리스너 설정이 전혀 걸려있지 않았으나, 해당 리소스를 적용하자 순식간에 메시 내의 모든 서비스에 대해서, 15443포트로 들어오는 트래픽을 SNI 패스스루 시키도록 조건이 걸렸다.
이제 드디어 멀티 클러스터 간 연결이 성공하는 모습을 볼 수 있게 됐다!
kwest exec -ti -n istioinaction debug -- curl catalog.istioinaction.svc.cluster.local/items/1
제대로 검증하기 위해, 외부라고 상정했던 mypc 컨테이너를 이용해 webapp을 거쳐 catalog의 정보를 가져오자.
EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
de curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog | jq
성공적으로 요청이 수행되는 모습을 볼 수 있다!
키알리 세팅
다음으로 멀티 클러스터 관측가능성 세팅을 해보자.[6]
이것도 west 클러스터가 기준이라 생각하고, west의 키알리가 east의 정보를 받아갈 수 있도록 세팅할 것이다.
프로메테우스 페더레이션 세팅
가장 먼저 해줄 것은 역시 프로메테우스 세팅이다.
문서에 따르면 메트릭이 먼저 하나의 소스로부터 수집돼야 한다고 나온다.
그래서 이를 위한 세팅을 해줘야 한다.[7]
이스티오 공식 문서에서 가르쳐 주는 방식은 federation인데, 메트릭을 모으는 방법은 다양하다.
공식 문서에서 가르쳐주는 방식으로 먼저 도전해본다.
아직 익숙하지 않을 때라 섣부른 도전은 지양하겠다.
나중에 thanos를 합치는 식으로 생각은 해볼 수 있을 듯.
일단 federation 방식은 secret 리소스를 만들면 오퍼레이터가 알아서 이를 인식하여 경로에 지정된 메트릭을 노출한다.[8]
(프메 스택 설치 코드는 레포 참고. 별 거 없다.)
# federate metrics from east to west
EXTERNAL_IP=$(keast get svc -n monitoring prom-kube-prometheus-stack-prometheus -ojsonpath="{.status.loadBalancer.ingress[0].ip}") \
REMOTE=east-cluster CURRENT=west-cluster \
envsubst '${EXTERNAL_IP} ${REMOTE} ${CURRENT}' < observability/prom-federation.yaml > west-fed.yaml
kwest -n monitoring create secret generic west-fed --from-file=west-fed.yaml
kwest patch prometheus -n monitoring prom-kube-prometheus-stack-prometheus \
--type=merge -p '{"spec": {"additionalScrapeConfigs":{"name": "west-fed", "key": "west-fed.yaml"}}}'
envsubst를 이용해 변수들을 특정 값으로 치환하여 새 파일을 만들고, 해당 파일을 시크릿으로 만드는 것을 볼 수 있다.
- job_name: 'federate-${REMOTE}'
scrape_interval: 15s
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="monitoring/envoy-stats-monitor"}'
static_configs:
- targets:
- '${EXTERNAL_IP}:9090'
labels:
cluster: '${REMOTE}'
- job_name: 'federate-local'
honor_labels: true
metrics_path: '/federate'
metric_relabel_configs:
- replacement: '${CURRENT}'
target_label: cluster
static_configs:
- targets:
- 'localhost:9090'
params:
'match[]':
- '{job="monitoring/envoy-stats-monitor"}'
해당 파일은 이렇게 프로메테우스 설정 파일이 담겨있다.
사실 별 게 아니라 그저 federate라는 경로로 메트릭을 노출하는 것밖에 없다.
그런데 조금 형식적인 이름을 붙여서 한다는 것이 주목할 점이다.
초반에 그냥 예제 따라하다가 삽질한 흔적을 남긴다.
공식 문서에 나온 그대로 따라서 넣었을 때는 이렇게 에러가 발생하는데, 사실 어느 정도 당연한 게 로컬의 메트릭을 federate 경로로, 그것도 이스티오 컨트롤 플레인에서 바로 땡겨봤자 나올 게 없기 때문이다.
파라미터를 수정해서 job이 무엇인지를 더 명확하게 잡으니까 비로소 해결됐다.
east 클러스터에서 debug 파드로 계속 요청을 만드는데, 해당 값이 west 클러스터의 프로메테우스에서 추적된다면 성공이다.
(근데 source, destination 정보가 클러스터를 넘나드는 과정에서 계속 소실되는 것 같다.)
이러면 키알리에서 제대로 추적이 될까?
키알리 시각화
키알리의 경우 공식 문서에서 조금이나마 간단하게 멀티 클러스터 세팅을 할 수 있도록 스크립트를 지원하고 있다.
curl -L -o kiali-prepare-remote-cluster.sh https://raw.githubusercontent.com/kiali/kiali/master/hack/istio/multicluster/kiali-prepare-remote-cluster.sh
chmod +x kiali-prepare-remote-cluster.sh
다른 클러스터 간 통신이 가능하도록 시크릿을 만들고 권한 세팅을 하는 리소스를 만들어준다.
해당 스크립트는 kubeconfig의 context를 기반으로 클러스터를 구분하기 때문에, 간단하게 config 파일을 손 봐준다.
기존에 사용하던 두 config 파일을 잘 버무려서 넣어주면 된다.
그리고 나서 적용하면..
이거 직접 만들었으면 얼마나 피곤했을까...?
자, 세팅이 끝났다.
두 개의 클러스터에서 통신이 이뤄지고 있다는 것을 명시적으로 확인할 수 있다!
참고로 이건 가시다님의 자료인데, 클러스터 통합 세팅을 하지 않으면 통합적인 관점에서 트래픽을 추적하는 것이 어렵다.
원래는 west 클러스터, east 클러스터 각각에서 표시되던 트래픽 정보를 이제 한 눈에 확인할 수 있게 된 것이다.
east 클러스터를 보면 파란색 선이 표시되는 것을 볼 수 있다.
해당 선은 TCP 연결을 나타내는 선으로, 실제 패킷이 해당 경로를 통해 들어오고 있다는 것을 보여준다.
반면 초록색은 워크로드 기준으로 이어지는 흐름을 나타낸 것이다.
west 클러스터에서는 실제 패킷의 흐름과 서비스 간의 트래픽 흐름 사이 간극이 존재하지 않았기에 하나의 선으로 표현됐다.
그러나 east 클러스터는 실제 트래픽은 동서 게이트웨이가 받아서 라우팅하나 해당 게이트웨이는 패스스루를 하여 카탈로그로 트래픽이 제대로 이어지도록 도와주는 역할만 하기에 위와 같이 그림이 나타나게 된다.
참고로 미리 찍어두지는 못 했지만, 사실 프로메테우스를 세팅한 시점에서 이미 트래픽 그래프는 두 클러스터를 전부 표시하고 있었다.
어차피 결국 키알리가 받아오는 관측 정보는 프로메테우스 메트릭으로부터 비롯되기 때문이다.
키알리에서 다른 클러스터로 접근할 수 있도록 하여 얻어낸 것은 멀티 클러스터로서 다른 클러스터의 정보를 조금 더 구체화시켜준 정도의 의미밖에 없다.
그래서 구체적으로 뭐가 바뀌었는가?
일단 클러스터가 무엇이 있는지 표시된다.
어플리케이션 탭에 가면 모든 클러스터의 앱들을 살펴볼 수 있다.
메시 상태 정보에서도 각 메시의 정보가 확인된다.
예거 세팅 - 실패
예거는 시도를 해봤는데, 왜인지 모르게 성공하지 못했다.
각각의 클러스터에 세팅한 다음에 모아야 하나?
처음 생각으로는 각 클러스터가 하나의 예거를 대상으로 트레이스 데이터를 보내주기만 하면 될 것이라 생각했는데, east 클러스터에서는 제대로 정보가 전달되지 않는다.
extensionProviders
필드 자체가 외부의 ip를 등록할 수 있는 구조가 아니라 이런 사용 방식이 잘못된 것 같다는 생각도 들었다.
그래서 스텁 서비스와 서비스 엔트리도 만들어봤지만 소용이 없었다.
정확한 이유를 탐구해봐야겠지만, 현재로서는 각 클러스터 별로 예거를 세팅하고 이를 모으는 식으로 시도를 해봐야할 것 같다.
https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-operator
간단 실습 - 지역 인식 로드밸런싱
이제 멀티 클러스터를 구축했으니, 지역 인지 라우팅을 마지막으로 실습해보고 끝낼 것이다.
세팅 자체는 단순하다.
한 서비스가 두 클러스터의 워크로드를 바라보는 상황을 만들고 로드밸런싱이 수행되는지 확인해보는 것이다.
kwest apply -f ch12/locality-aware/west/simple-backend-deployment.yaml
kwest apply -f ch12/locality-aware/west/simple-backend-svc.yaml
kwest apply -f ch12/locality-aware/west/simple-backend-gw.yaml
kwest apply -f ch12/locality-aware/west/simple-backend-vs.yaml
kwest apply -f ch12/locality-aware/west/simple-backend-dr.yaml
keast apply -f ch12/locality-aware/east/simple-backend-deployment.yaml
keast apply -f ch12/locality-aware/east/simple-backend-svc.yaml
simple backend라고 하는 아주 단순한 백엔드를 배포한다.
위 양식을 세팅하면 각 클러스터 별로 자신의 정보를 밝히는 워크로드가 배치된다.
EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
de curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
simple backend는 현재 두 클러스터에 모두 배치된 상태이다.
그럼 해당 서비스로 가는 트래픽은 두 클러스터에 전부 갈 수 있어야 한다는 말이다.
west 클러스터 아무 엔보이의 클러스터 정보를 뜯어보면, 보다시피 두 가지의 엔드포인트가 있는 것을 볼 수 있다.
하나는 클러스터 내부의 백엔드 주소, 다른 하나는 동서 게이트웨이 주소가 잡히고 있다.
이를 통해 simple backend로 가는 트래픽은 다른 클러스터의 백엔드로 갈 경우 게이트웨이를 타고 들어가게 될 것이다.
이전 글, 다음 글
다른 글 보기
이름 | index | noteType | created |
---|---|---|---|
1W - 서비스 메시와 이스티오 | 1 | published | 2025-04-10 |
1W - 간단한 장애 상황 구현 후 대응 실습 | 2 | published | 2025-04-10 |
1W - Gateway API를 활용한 설정 | 3 | published | 2025-04-10 |
1W - 네이티브 사이드카 컨테이너 이용 | 4 | published | 2025-04-10 |
2W - 엔보이 | 5 | published | 2025-04-19 |
2W - 인그레스 게이트웨이 실습 | 6 | published | 2025-04-17 |
3W - 버츄얼 서비스를 활용한 기본 트래픽 관리 | 7 | published | 2025-04-22 |
3W - 트래픽 가중치 - flagger와 argo rollout을 이용한 점진적 배포 | 8 | published | 2025-04-22 |
3W - 트래픽 미러링 패킷 캡쳐 | 9 | published | 2025-04-22 |
3W - 서비스 엔트리와 이그레스 게이트웨이 | 10 | published | 2025-04-22 |
3W - 데스티네이션 룰을 활용한 네트워크 복원력 | 11 | published | 2025-04-26 |
3W - 타임아웃, 재시도를 활용한 네트워크 복원력 | 12 | published | 2025-04-26 |
4W - 이스티오 메트릭 확인 | 13 | published | 2025-05-03 |
4W - 이스티오 메트릭 커스텀, 프로메테우스와 그라파나 | 14 | published | 2025-05-03 |
4W - 오픈텔레메트리 기반 트레이싱 예거 시각화, 키알리 시각화 | 15 | published | 2025-05-03 |
4W - 번외 - 트레이싱용 심플 메시 서버 개발 | 16 | published | 2025-05-03 |
5W - 이스티오 mTLS와 SPIFFE | 17 | published | 2025-05-11 |
5W - 이스티오 JWT 인증 | 18 | published | 2025-05-11 |
5W - 이스티오 인가 정책 설정 | 19 | published | 2025-05-11 |
6W - 이스티오 설정 트러블슈팅 | 20 | published | 2025-05-18 |
6W - 이스티오 컨트롤 플레인 성능 최적화 | 21 | published | 2025-05-18 |
8W - 가상머신 통합하기 | 22 | published | 2025-06-01 |
8W - 엔보이와 iptables 뜯어먹기 | 23 | published | 2025-06-01 |
9W - 앰비언트 모드 구조, 원리 | 24 | published | 2025-06-07 |
9W - 앰비언트 헬름 설치, 각종 리소스 실습 | 25 | published | 2025-06-07 |
7W - 이스티오 메시 스케일링 | 26 | published | 2025-06-09 |
7W - 엔보이 필터를 통한 기능 확장 | 27 | published | 2025-06-09 |
관련 문서
지식 문서, EXPLAIN
이름11 | is-folder | 생성 일자 |
---|---|---|
E-이스티오 가상머신 통합 | false | 2025-06-01 13:32 |
E-이스티오 DNS 프록시 동작 | false | 2025-06-01 12:33 |
E-이스티오에서 엔보이 기능 확장하기 | false | 2025-06-01 14:06 |
E-이스티오 메시 스케일링 | false | 2025-06-08 23:41 |
메시 배포 모델 | false | 2025-05-21 13:36 |
Istio WorkloadGroup | false | 2025-05-26 09:27 |
Istio WorkloadEntry | false | 2025-05-26 09:32 |
Istio WasmPlugin | false | 2025-04-21 20:41 |
Istio Extenisibility | true | 2025-05-26 09:26 |
Istio EnvoyFilter | false | 2025-04-21 20:36 |
E-이스티오의 데이터 플레인 트래픽 세팅 원리 | false | 2025-05-27 21:55 |
기타 문서
Z0-연관 knowledge, Z1-트러블슈팅 Z2-디자인,설계, Z3-임시, Z5-프로젝트,아카이브, Z8,9-미분류,미완이름5 | 코드 | 타입 | 생성 일자 |
---|---|---|---|
8W - 엔보이와 iptables 뜯어먹기 | Z8 | published | 2025-06-01 12:14 |
엔보이에 와즘 플러그인 적용해보기 | Z8 | topic | 2025-06-09 02:29 |
4W - 이스티오 메트릭 커스텀, 프로메테우스와 그라파나 | Z8 | published | 2025-05-03 22:16 |
7W - 엔보이 필터를 통한 기능 확장 | Z8 | published | 2025-06-09 02:30 |
8W - 가상머신 통합하기 | Z8 | published | 2025-06-01 12:11 |
참고
https://istio.io/latest/docs/ops/configuration/traffic-management/dns-proxy/#sidecar-mode ↩︎
https://developer.hashicorp.com/vault/tutorials/archive/kubernetes-cert-manager ↩︎
https://istio.io/latest/docs/reference/commands/pilot-agent/#pilot-agent-istio-iptables ↩︎
https://istio.io/latest/docs/reference/config/networking/gateway/#ServerTLSSettings-TLSmode ↩︎
https://istio.io/latest/docs/ops/configuration/telemetry/monitoring-multicluster-prometheus/ ↩︎
https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/additional-scrape-config.md ↩︎