본문 바로가기
WIKI 기술 지식 베이스

멀티클러스터 통신

원문 보기 위키 갱신

멀티클러스터 통신 (Multi-cluster communication)

두 개의 Kubernetes 클러스터가 서로의 서비스에 접근하도록 Linkerd를 설치·구성하는 방법을 차근차근 따라 해 보는 가이드예요.

출처: Linkerd Multi-cluster communication

본문

이 가이드는 두 클러스터가 양쪽에 호스팅된 서비스와 통신할 수 있도록 Linkerd를 설치하고 구성하는 방법을 안내해요. 여기에는 많은 동작 요소와 개념이 있으므로, 내부적으로 어떻게 동작하는지 설명하는 소개 문서를 먼저 읽어 보는 것이 좋아요. 이 가이드를 마치면 서로 다른 클러스터에 있는 서비스 사이에서 트래픽을 분할하는 방법을 이해하게 될 거예요.

높은 수준에서 다음과 같은 작업을 하게 돼요.

  • 공유 트러스트 앵커를 가진 두 클러스터에 Linkerd와 Linkerd Viz를 설치.
  • 클러스터 준비.
  • 클러스터 연결(link).
  • 데모 설치.
  • 가시성 제어를 위해 데모 서비스를 내보내기(export).
  • 클러스터 보안 검증.
  • 소스 클러스터(west)의 pod에서 대상 클러스터(east)로 트래픽 분할.

사전 준비 (Prerequisites)

  • 두 개의 클러스터. 이 가이드에서는 east와 west라고 부를게요. 가이드를 진행하면서 블로그 포스트도 함께 따라 해 보세요! 개발용으로 가장 쉬운 방법은 노트북에서 kind 또는 k3d 클러스터를 로컬로 실행하고, AKS 같은 클라우드 제공업체에 하나를 원격으로 실행하는 거예요.
  • 각 클러스터는 kubectl 컨텍스트로 구성되어야 해요. 이 가이드를 따라 하기 쉽도록 east와 west라는 이름을 사용하길 권장해요. kubectl로 컨텍스트 이름을 바꾸는 것은 쉬우니, 계속 이 이름을 유지해야 한다고 생각하지 마세요.
  • 두 클러스터 모두에서 상승된 권한이 필요해요. 서비스 계정을 만들고 확장된 권한을 부여할 거니까, 테스트 클러스터에서 그렇게 할 수 있어야 해요.
  • east 클러스터에서 LoadBalancer 유형의 서비스 지원. 클러스터 제공업체의 문서를 확인하거나 inlets를 살펴보세요. 이것이 west 클러스터가 게이트웨이를 통해 east와 통신하는 데 사용할 것이에요.

Linkerd와 Linkerd Viz 설치

Two Clusters

Linkerd는 서로 통신하는 모든 클러스터의 설치 간에 공유 트러스트 앵커가 존재해야 해요. 이는 클러스터 간 트래픽을 암호화하고 게이트웨이에 도달하는 요청을 인가해서, 클러스터가 공개 인터넷에 열려 있지 않도록 하는 데 사용돼요. linkerd가 모든 것을 생성하게 하는 대신, 우리는 인증 정보를 직접 생성해 install 명령의 구성으로 사용해야 해요.

이 인증서를 생성하는 데는 step CLI를 사용하는 걸 좋아해요. openssl을 선호한다면 그것을 사용해도 좋아요! step으로 트러스트 앵커를 생성하려면 다음을 실행할 수 있어요.

step certificate create root.linkerd.cluster.local root.crt root.key \
  --profile root-ca --no-password --insecure

이 인증서는 모든 클러스터 간 공통 신뢰의 기반을 형성해요. 각 프록시는 이 인증서의 복사본을 받아 mTLS 핸드셰이크의 일부로 peer로부터 받은 인증서를 검증하는 데 사용해요. 공통 신뢰 기반이 준비됐으니, 이제 각 클러스터에서 프록시에 인증서를 발급하는 데 사용할 인증서를 생성해야 해요. 이 모든 것이 어떻게 동작하는지 더 깊이 이해하고 싶다면 deep dive 문서를 확인하세요.

우리가 생성한 트러스트 앵커는 자체 서명된 인증서이며, 새 인증서(인증 기관, certificate authority)를 만드는 데 사용할 수 있어요. 트러스트 앵커를 사용해 발급자(issuer) 인증 정보를 생성하려면 다음을 실행하세요.

step certificate create identity.linkerd.cluster.local issuer.crt issuer.key \
  --profile intermediate-ca --not-after 8760h --no-password --insecure \
  --ca root.crt --ca-key root.key

클러스터의 identity 서비스는 여기서 생성한 인증서와 키를 사용해 각 개별 프록시가 사용하는 인증서를 생성해요. 이 가이드에서는 각 클러스터에 동일한 발급자 인증 정보를 사용하지만, 각 클러스터마다 별도의 것을 두는 것이 좋아요. 자세한 내용은 인증서 문서를 읽어 보세요.

유효한 트러스트 앵커와 발급자 인증 정보가 준비됐으니, 이제 west와 east 클러스터에 Linkerd를 설치할 수 있어요.

# first, install the Linkerd CRDs in both clusters
linkerd install --crds \
  | tee \
    >(kubectl --context=west apply -f -) \
    >(kubectl --context=east apply -f -)

# then install the Linkerd control plane in both clusters
linkerd install \
  --identity-trust-anchors-file root.crt \
  --identity-issuer-certificate-file issuer.crt \
  --identity-issuer-key-file issuer.key \
  | tee \
    >(kubectl --context=west apply -f -) \
    >(kubectl --context=east apply -f -)

그다음 Linkerd Viz를 설치해요.

for ctx in west east; do
  linkerd --context=${ctx} viz install | \
    kubectl --context=${ctx} apply -f - || break
done

install의 출력이 각 클러스터에 적용되어 올라올 거예요! check로 모든 것이 성공적으로 올라왔는지 검증할 수 있어요.

for ctx in west east; do
  echo "Checking cluster: ${ctx} ........."
  linkerd --context=${ctx} check || break
  echo "-------------"
done

클러스터 준비하기

Preparation

클러스터 간 트래픽을 라우팅하기 위해 Linkerd는 Kubernetes 서비스를 활용하므로, 애플리케이션 코드를 바꿀 필요도 없고 배울 것이 새로 없어요. 이를 위해서는 들어오는 요청을 올바른 내부 서비스로 라우팅하는 게이트웨이 구성 요소가 필요해요. 게이트웨이는 LoadBalancer 유형의 Service로 공개 인터넷에 노출돼요. Linkerd의 mTLS(공유 트러스트 앵커 기반)를 통해 검증된 요청만 이 게이트웨이를 통과할 수 있어요. 이게 왜 중요한지 더 자세히 알고 싶다면 multicluster Kubernetes 설계(architecting for multicluster Kubernetes) 문서를 확인하세요.

west와 east 양쪽에 multicluster 구성 요소를 설치하려면 다음을 실행할 수 있어요.

for ctx in west east; do
  echo "Installing on cluster: ${ctx} ........."
  linkerd --context=${ctx} multicluster install | \
    kubectl --context=${ctx} apply -f - || break
  echo "-------------"
done

Components

linkerd-multicluster 네임스페이스에 설치되는 게이트웨이는 Linkerd 프록시가 주입된 간단한 pause 컨테이너예요. 인바운드 측에서 Linkerd는 연결이 트러스트 앵커에 속한 TLS 인증서를 사용하는지 검증한 다음 아웃바운드 연결을 처리해요. 이 시점에서 Linkerd 프록시는 데이터 플레인의 다른 프록시처럼 동작하며 요청을 올바른 서비스로 전달해요. 게이트웨이가 성공적으로 올라오는지 다음으로 확인하세요.

for ctx in west east; do
  echo "Checking gateway on cluster: ${ctx} ........."
  kubectl --context=${ctx} -n linkerd-multicluster \
    rollout status deploy/linkerd-gateway || break
  echo "-------------"
done

로드 밸런서가 공개 IP 주소를 할당했는지도 다시 한 번 확인해 보세요.

for ctx in west east; do
  printf "Checking cluster: ${ctx} ........."
  while [ "$(kubectl --context=${ctx} -n linkerd-multicluster get service -o 'custom-columns=:.status.loadBalancer.ingress[0].ip' --no-headers)" = "" ]; do
      printf '.'
      sleep 1
  done
  printf "\n"
done

이제 모든 클러스터가 multicluster 컨트롤 플레인을 실행 중이며 서비스 미러링을 시작할 준비가 됐어요. 이제 클러스터를 연결해 볼게요!

클러스터 연결하기 (Linking the clusters)

Link

west가 east의 서비스를 미러링하려면, east에서 내보내지는 서비스를 감시할 수 있는 자격 증명이 west 클러스터에 필요해요. 누구나 클러스터에서 무엇이 실행 중인지 들여다볼 수 있게 하고 싶지는 않을 테니까요! 자격 증명은 서비스 미러를 인증하는 서비스 계정과, 서비스 감시를 허용하는 ClusterRole 및 ClusterRoleBinding으로 구성돼요. 전체적으로 서비스 미러 구성 요소는 이 자격 증명을 사용해 east(또는 대상) 클러스터의 서비스를 감시하고 자신(west)에 추가/제거해요. linkerd multicluster install의 일부로 기본 집합이 추가되지만, 각 클러스터마다 별도의 자격 증명을 원한다면 linkerd multicluster allow를 실행할 수 있어요.

다음 단계는 west를 east에 연결하는 것이에요. 이를 위해 먼저 multicluster 확장이 east의 원하는 서비스를 미러링하는 데 책임이 있는 컨트롤러를 배포하도록 구성해야 해요. 이는 컨트롤러 사양이 담긴 구성 파일을 만들고, 구성 파일을 전달해 install 명령을 다시 실행하는 방식으로 이뤄져요.

cat  values.yaml
controllers:
- link:
    ref:
      name: east
EOF
linkerd --context=west multicluster install -f values.yaml |
  kubectl --context=west apply -f -

다음으로 대상(east) 클러스터의 Kubernetes API에 접근할 수 있는 kubeconfig가 포함된 자격 증명 시크릿을 제공해야 해요. 이를 위해 동일한 시크릿 두 개를 만들어야 해요. 하나는 linkerd-multicluster 네임스페이스에, 다른 하나는 linkerd 네임스페이스에 배포해요. 또한 서비스 미러링을 구성할 Link 커스텀 리소스(CR)를 제공해야 해요. 이 CR에는 게이트웨이 주소, 게이트웨이 identity, 미러링할 서비스를 식별하는 데 사용되는 라벨 셀렉터 같은 세부 정보가 포함돼요. 컨트롤러는 Link CR과 자격 증명을 활용해 지정된 라벨 셀렉터와 일치하는 대상 클러스터의 서비스를 찾아 소스(로컬) 클러스터로 복제해요. 이런 리소스 생성을 간소화하려면(또는 초기 모델을 잡으려면) 다음 명령어를 사용할 수 있어요.

linkerd --context=east multicluster link-gen --cluster-name east |
  kubectl --context=west apply -f -

Linkerd는 현재 east 컨텍스트를 살펴보고, 서버 위치와 CA 번들을 포함하는 cluster 구성을 추출해요. 그런 다음 ServiceAccount 토큰을 가져와 이러한 구성 조각들을 시크릿이 된 kubeconfig로 병합해요.

check를 다시 실행하면 서비스 미러가 이 시크릿을 발견하고 east에 도달할 수 있는지 확인할 수 있어요.

linkerd --context=west multicluster check

또한 east 게이트웨이가 목록에 나타나야 해요.

linkerd --context=west multicluster gateways

참고

link는 두 클러스터가 로컬에서 사용하는 것과 동일한 구성으로 서로 연결된다고 가정해요. 그렇지 않은 경우 link에 --api-server-address 플래그를 사용해야 해요.

테스트 서비스 설치하기

Topology

이제 이 모든 것을 테스트할 시간이에요! 첫 단계는 미러링할 서비스를 몇 개 추가하는 거예요. 두 클러스터에 추가하려면 다음을 실행할 수 있어요.

for ctx in west east; do
  echo "Adding test services on cluster: ${ctx} ........."
  kubectl --context=${ctx} create ns test
  kubectl --context=${ctx} apply \
    -n test -k "github.com/linkerd/website/multicluster/${ctx}/"
  kubectl --context=${ctx} -n test \
    rollout status deploy/podinfo || break
  echo "-------------"
done

이제 각 클러스터에서 두 개의 디플로이먼트(frontend와 podinfo)를 실행하는 test 네임스페이스가 생길 거예요. podinfo는 각 클러스터에서 이름과 색상이 조금 다르게 구성되어 있어서, 요청이 어디로 가는지 구분할 수 있어요.

지금 west 클러스터에서 어떻게 보이는지 확인하려면 다음을 실행할 수 있어요.

kubectl --context=west -n test port-forward svc/frontend 8080

West Podinfo

http://localhost:8080에서 podinfo 랜딩 페이지를 볼 수 있고, west 클러스터에서 지금 어떻게 보이는지 확인할 수 있어요. 또는 curl http://localhost:8080을 실행하면 다음과 같은 JSON 응답을 반환해요.

{
  "hostname": "podinfo-5c8cf55777-zbfls",
  "version": "4.0.2",
  "revision": "b4138fdb4dce7b34b6fc46069f70bb295aa8963c",
  "color": "#6c757d",
  "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
  "message": "greetings from west",
  "goos": "linux",
  "goarch": "amd64",
  "runtime": "go1.14.3",
  "num_goroutine": "8",
  "num_cpu": "4"
}

message가 west 클러스터 이름을 참조하는 것을 주목하세요.

서비스 내보내기 (Exporting the services)

민감한 서비스가 미러링되지 않고, 서비스 생성/삭제로 인해 클러스터 성능이 영향받지 않도록 하려면 서비스가 명시적으로 내보내져야 해요. 이 가이드에서는 east 클러스터의 podinfo 서비스를 west 클러스터로 내보낼 거예요. 이를 위해 먼저 east 클러스터에서 podinfo 서비스를 내보내야 해요. mirror.linkerd.io/exported 라벨을 추가하면 됩니다.

kubectl --context=east label svc -n test podinfo mirror.linkerd.io/exported=true

참고

linkerd multicluster link-gen 명령의 --selector 플래그를 사용하거나 Link 리소스를 편집해서 다른 라벨 셀렉터를 구성할 수 있어요.

컨트롤러가 방금 생성한 서비스를 확인해 보세요.

kubectl --context=west -n test get svc podinfo-east

아키텍처 문서에서 기억하듯이, 컨트롤러는 서비스를 이동시키는 것 이상의 일을 해요. 미러링된 서비스의 엔드포인트도 관리해요. 올바르게 설정되었는지 확인하려면 west의 엔드포인트를 확인하고 east의 게이트웨이 공개 IP 주소와 일치하는지 검증할 수 있어요.

kubectl --context=west -n test get endpoints podinfo-east \
  -o 'custom-columns=ENDPOINT_IP:.subsets[*].addresses[*].ip'
kubectl --context=east -n linkerd-multicluster get svc linkerd-gateway \
  -o "custom-columns=GATEWAY_IP:.status.loadBalancer.ingress[*].ip"

이 시점에서 west 클러스터에서 east의 podinfo 서비스를 호출할 수 있어요. 이를 위해서는 클라이언트가 메시가 적용되어야 하므로, frontend pod 안에서 curl을 실행해 볼게요.

kubectl --context=west -n test exec -c nginx -it \
  $(kubectl --context=west -n test get po -l app=frontend \
    --no-headers -o custom-columns=:.metadata.name) \
  -- /bin/sh -c "apk add curl && curl http://podinfo-east:9898"

greeting from east 메시지가 보일 거예요! west에서 실행 중인 frontend pod의 요청이 투명하게 east로 전달되고 있어요. 이전 단계에서 계속 포트 포워딩 중이라면 curl http://localhost:8080/east로도 도달할 수 있어요. 그 호출을 몇 번 해 보면 linkerd viz stat-outbound에서 메트릭을 얻을 수 있어요.

linkerd --context=west -n test viz stat-outbound deploy/frontend

또한 여기서 무슨 일이 일어나고 있는지 파악할 수 있는 grafana 대시보드도 제공해요 (먼저 grafana 설치 안내를 따라 Linkerd 대시보드가 프로비저닝된 grafana를 준비하세요). linkerd --context=west viz dashboard를 실행하고 Grafana로 이동하면 접근할 수 있어요.

보안 (Security)

기본적으로 요청은 공개 인터넷을 통해 전송돼요. Linkerd는 자동 mTLS를 클러스터 간으로 확장해 공개 인터넷을 통해 전송되는 통신이 암호화되도록 해요. 이를 검증하는 방법에 대한 심층 분석은 문서를 확인하세요. 빠르게 확인하려면 다음을 실행할 수 있어요.

linkerd --context=west -n test viz tap deploy/frontend | \
  grep "$(kubectl --context=east -n linkerd-multicluster get svc linkerd-gateway \
    -o "custom-columns=GATEWAY_IP:.status.loadBalancer.ingress[*].ip")"

tls=true는 요청이 암호화되고 있다는 뜻이에요!

참고

linkerd viz edges는 구체적인 리소스에서 작동하고 두 클러스터를 동시에 볼 수 없기 때문에, 현재 east와 west 사이의 pod 간 edge를 표시할 수 없어요. 이것이 여기서 mTLS를 검증하는 데 tap을 사용하는 이유예요.

모든 요청이 암호화되도록 하는 것 외에도, 클러스터로 들어오는 임의의 요청을 차단하는 것도 중요해요. 이를 위해 요청이 메시 안의 클라이언트에서 오는지 검증해요. 이 검증을 위해 클러스터 간 공유 트러스트 앵커에 의존해요. 메시 밖의 클라이언트가 접근하면 어떻게 되는지 보려면 다음을 실행할 수 있어요.

kubectl --context=west -n test run -it --rm --image=alpine:3 test -- \
  /bin/sh -c "apk add curl && curl -vv http://podinfo-east:9898"

트래픽 분할 (Traffic Splitting)

Traffic Split

서비스가 자동으로 클러스터에 나타나고 명시적으로 주소 지정이 가능한 것은 꽤 유용하지만, 이는 다중 클러스터 운영의 한 가지 사용 사례에 불과해요. 멀티클러스터의 또 다른 시나리오는 장애 조치(failover)예요. 장애 조치 시나리오에서는 구성을 업데이트할 시간이 없어요. 대신 애플리케이션은 그대로 두고 라우팅만 변경할 수 있어야 해요. 이것이 카나리 배포 방식과 아주 비슷하게 들린다면, 맞는 생각이에요!

TrafficSplit을 사용하면 여러 서비스 사이의 가중치를 정의하고 그 사이에서 트래픽을 분할할 수 있어요. 장애 조치 시나리오에서는 다른 클러스터에 과부하가 걸리거나 추가된 지연 시간으로 SLO를 위반하지 않도록 천천히 수행하고 싶을 거예요. 이 시나리오에서 작동시키려면 west와 east의 podinfo 서비스 사이에서 분할해 볼게요. 이를 구성하려면 다음을 실행하세요.

kubectl --context=west apply -f - apiVersion: split.smi-spec.io/v1alpha1
kind: TrafficSplit
metadata:
  name: podinfo
  namespace: test
spec:
  service: podinfo
  backends:
  - service: podinfo
    weight: 50
  - service: podinfo-east
    weight: 50
EOF

이제 podinfo에 대한 모든 요청은 50% 확률로 podinfo-east 클러스터로, 나머지 50%는 로컬 podinfo 서비스로 전달돼요. podinfo-east로 보내진 요청은 east 클러스터에 도달하므로, 이제 사실상 트래픽의 50%를 west에서 east로 장애 조치한 셈이에요.

아직 port-forward를 실행 중이라면 브라우저를 http://localhost:8080으로 보낼 수 있어요. 페이지를 새로고침하면 두 클러스터가 모두 보일 거예요. 또는 명령줄 방식으로 curl localhost:8080을 실행하면 west와 east에서 온 메시지를 볼 수 있어요.

Cross Cluster Podinfo

또한 메트릭으로 무슨 일이 일어나는지 지켜볼 수 있어요. 소스 쪽(west)을 보려면 다음을 실행할 수 있어요.

linkerd --context=west -n test viz stat trafficsplit

대상(east) 쪽에서도 다음을 실행해 볼 수 있어요.

linkerd --context=east -n test viz stat \
  --from deploy/linkerd-gateway \
  --from-namespace linkerd-multicluster \
  deploy/podinfo

대시보드도 있어요! linkerd viz dashboard를 실행하고 브라우저를 localhost:50750으로 보내세요.

Cross Cluster Podinfo

정리 (Cleanup)

multicluster 컨트롤 플레인을 정리하려면 다음을 실행할 수 있어요.

# Delete the link CR
$ kubectl --context=west -n linkerd-multicluster delete links east
# Delete the test namespace and uninstall multicluster
$ for ctx in west east; do \
  kubectl --context=${ctx} delete ns test; \
  linkerd --context=${ctx} multicluster uninstall | kubectl --context=${ctx} delete -f - ; \
done

Linkerd 설치까지 제거하고 싶다면 다음을 실행하세요.

for ctx in west east; do
  linkerd --context=${ctx} viz uninstall | kubectl --context=${ctx} delete -f -
  linkerd --context=${ctx} uninstall | kubectl --context=${ctx} delete -f -
done

더 알아보기 (Learn more)