[Linkerd] 멀티클러스터 통신 구성
Linkerd MultiCluster는 여러 Kubernetes 클러스터를 하나의 서비스 메시로 연결하여, 클러스터 간 안전하고 신뢰성 높은 통신을 가능하게 한다. 이를 통해 서로 다른 클러스터에 있는 Pod 간의 통신이 가능해지며, 고가용성, 확장성, 격리 등 다양한 이점을 제공한다.
멀티클러스터 통신 흐름은 아래와 같다.

본 글에서는 멀티클러스터 간 통신 및 트래픽 흐름을 실습을 통해 확인해 보려 한다.
전제 조건
- 두 개의 Kubernetes 클러스터
- 두 개의 클러스터에 Linkerd-Control-Plane, Linkerd-viz, Cert-manager, Linkerd-crd, ngress-nginx-controller 구성
- 두 개의 클러스터에 linkerd cli 툴 설치
linkerd 관련 구성은 [Service Mesh/Linkerd] - Linkerd 설치] 글을 참고하면 된다.
실습
1. 공유 Trust anchor를 위한 인증서 업데이트

각 cluster의 ca.crt 파일 추출한다.
## cluster1, cluster2 에서 진행
$ kubectl -n linkerd get cm linkerd-config -ojsonpath="{.data.values}" | \
yq e .identityTrustAnchorsPEM -
추출된 cluster1, cluster2의 ca.crt 파일로 linkerd 설치 시 사용한 Helm values.yaml 파일에 추가한다.
## cluster1, cluster2 values.yaml
identityTrustAnchorsPEM: |
-----BEGIN CERTIFICATE-----
# ...
# cluster1 ca.crt파일
# ...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
# ...
# cluster2 ca.crt 파일
# ...
-----END CERTIFICATE-----
위 vaule.yaml 파일로 cluster1, cluster2 Helm 업그레이드를 한다.
$ helm upgrade linkerd-control-plane linkerd/linkerd-control-plane -n linkerd -f values.yaml --atomic
참고
linkerd 인증서를 변경했기 때문에 linkerd-proxy를 사용하는 pod는 전부 restart가 필요하다.
2. 멀티클러스터 통신을 위한 linkerd gateway 구성
cluster1, cluster2에서 멀티클러스터 통신용 linkerd-gateway를 구성한다.
$ helm install linkerd-multicluster -n linkerd-multicluster --create-namespace linkerd/linkerd-multicluster
linkerd-gateway의 service는 다른 클러스터와 통신이 되어야 하기 때문에 LoadBalancer 타입으로 생성된다.
$ kubectl get po -n linkerd-multicluster
NAME READY STATUS RESTARTS AGE
linkerd-gateway-556988bf96-cr88d 2/2 Running 0 30m
$ kubectl get svc -n linkerd-multicluster
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
linkerd-gateway LoadBalancer 172.20.52.163 xxxxxxxxxxxxxxxxt-2.elb.amazonaws.com 4143:31954/TCP,4191:30960/TCP 3h57m
cluster1에서 linkerd cli 도구를 이용해 자격 증명을 위한 내용을 추출하여 cluster2에 적용한다.
# cluster1에서 정보 추출
$ linkerd multicluster link --cluster-name cluster1 > cluster1.yaml
# cluster2에서 위에서 추출한 파일 적용
$ kubectl apply -f cluster1.yaml
위 명령은 서비스를 감시하는데 필요한 자격 증명을 위해 ServiceAccount와 ClusterRole, ClusterRoleBinding을 생성한다.
또한 Secret, Link 리소스 및 서비스 미러 컨트롤러를 생성한다.
- Secret : cluster1의 Kubernetes API에 액세스 할 수 있는 kubeconfig이다.
- Link : 서비스 미러링을 구성하는 사용자 정의 리소스이며, 게이트웨이 주소, 게이트웨이 식별자 및 미러링 할 서비스를 결정하는 데 사용할 Label selector 등을 포함한다.
- 서비스 미러 컨트롤러 : Link 및 Secret 정보를 사용하여 cluster1(대상) 클러스터에서 Label selector와 일치하는 서비스를 찾아 cluster2(로컬) 클러스터로 복사한다.
적용한 리소스들이 생성되었는지 확인한다.
$ kubectl get -f cluster1.yaml
NAME TYPE DATA AGE
secret/cluster-credentials-cluster1 mirror.linkerd.io/remote-kubeconfig 1 39s
NAME AGE
link.multicluster.linkerd.io/cluster1 39s
NAME CREATED AT
clusterrole.rbac.authorization.k8s.io/linkerd-service-mirror-access-local-resources-cluster1 2023-04-27T16:13:55Z
NAME ROLE AGE
clusterrolebinding.rbac.authorization.k8s.io/linkerd-service-mirror-access-local-resources-cluster1 ClusterRole/linkerd-service-mirror-access-local-resources-cluster1 39s
NAME CREATED AT
role.rbac.authorization.k8s.io/linkerd-service-mirror-read-remote-creds-cluster1 2023-04-27T16:13:55Z
NAME ROLE AGE
rolebinding.rbac.authorization.k8s.io/linkerd-service-mirror-read-remote-creds-cluster1 Role/linkerd-service-mirror-read-remote-creds-cluster1 39s
NAME SECRETS AGE
serviceaccount/linkerd-service-mirror-cluster1 1 39s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/linkerd-service-mirror-cluster1 1/1 1 1 39s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/probe-gateway-cluster1 ClusterIP 172.20.177.81 <none> 4191/TCP 39s
3. 서비스 내보내기
cluster1에서 static-server를 띄운다.
## cluster1
apiVersion: v1
kind: Service
metadata:
name: static-server
spec:
selector:
app: static-server
ports:
- protocol: TCP
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: static-server
spec:
replicas: 1
selector:
matchLabels:
app: static-server
template:
metadata:
name: static-server
labels:
app: static-server
annotations:
linkerd.io/inject: enabled
spec:
containers:
- name: static-server
image: wlsdn3004/http-echo:latest
resources:
requests:
memory: "64Mi"
cpu: "25m"
limits:
memory: "100Mi"
cpu: "50m"
args:
- -text="hello static-server"
- -listen=:8080
ports:
- containerPort: 8080
name: http
static-server의 service에 label을 추가하여 서비스를 내보낸다.
## cluster1
$ kubectl label svc static-server mirror.linkerd.io/exported=true
service/static-server labeled
cluster2에서 아래와 같이 service가 생성된 것을 확인할 수 있다.
## cluster2
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
static-server-cluster1 ClusterIP 172.20.244.165 <none> 8080/TCP 43s
static-server 서비스의 endpoint를 보면 cluster1의 linkerd gateway ip이다.
## cluster2
$ kubectl get ep static-server-cluster1
NAME ENDPOINTS AGE
static-server-cluster1 {cluster1-linkerd_gateway-ip}:4143 105s
cluister2의 linkerd 대시보드에서 cluster1의 1개의 서비스와 페어링 된 것을 확인할 수 있다.

4. 멀티클러스터 통신
cluster2에 static-client를 배포하고 호출 테스트 해보자.
## cluster2
apiVersion: v1
kind: Service
metadata:
name: static-client
spec:
selector:
app: static-client
ports:
- port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: static-client
name: static-client
spec:
replicas: 1
selector:
matchLabels:
app: static-client
template:
metadata:
annotations:
linkerd.io/inject: enabled
labels:
app: static-client
spec:
containers:
- name: static-client
image: wlsdn3004/curl:latest
command: ['/bin/sh', '-c', '--']
args: ['while true; do sleep 30; done;']
cluster1의 Pod와 정상적으로 통신하는 것을 확인할 수 있다.
## cluster2
$ curl static-server-cluster1:8080
"hello static-server1"
5. 구간별 패킷 흐름 검증
멀티클러스터 패킷 흐름을 linkerd cli 툴을 사용하여 검증해 보자.
cluster2의 static-client 기준 패킷
[src] static-client (10.79.40.243) -> [dst] cluster1 linkerd gateway LoadBalancer (1.1.1.1)

## cluster2
$ linkerd viz tap deploy/static-client2
req id=4:0 proxy=out src=10.79.40.243:56420 dst=1.1.1.1:4143 tls=true :method=GET :authority=static-server.default.svc.cluster.local:8080 :path=/
rsp id=4:0 proxy=out src=10.79.40.243:56420 dst=1.1.1.1:4143 tls=true :status=200 latency=7577µs
end id=4:0 proxy=out src=10.79.40.243:56420 dst=1.1.1.1:4143 tls=true duration=504µs response-length=23B
cluster1의 linkerd-gateway 기준 패킷
[src] worker node(192.168.6.144) -> [dst] static-server(192.168.6.226)

## cluster1
$ linkerd viz tap -n linkerd-multicluster deploy/linkerd-gateway
req id=7:0 proxy=out src=192.168.6.144:59570 dst=192.168.6.226:8080 tls=true :method=GET :authority=static-server.default.svc.cluster.local:8080 :path=/
rsp id=7:0 proxy=out src=192.168.6.144:59570 dst=192.168.6.226:8080 tls=true :status=200 latency=948µs
end id=7:0 proxy=out src=192.168.6.144:59570 dst=192.168.6.226:8080 tls=true duration=33µs response-length=23B
cluster1 static-server 기준 패킷
[src] linkerd-gateway(192.168.6.169) -> [dst] static-server(192.168.6.226)

## cluster1
$ linkerd viz tap -n linkerd-multicluster deploy/static-server
deployment.apps "static-server" not found
[root@ip-192-168-10-142 ~]# linkerd viz tap deploy/static-server
req id=3:0 proxy=in src=192.168.6.169:56942 dst=192.168.6.226:8080 tls=true :method=GET :authority=static-server.default.svc.cluster.local:8080 :path=/
rsp id=3:0 proxy=in src=192.168.6.169:56942 dst=192.168.6.226:8080 tls=true :status=200 latency=459µs
end id=3:0 proxy=in src=192.168.6.169:56942 dst=192.168.6.226:8080 tls=true duration=109µs response-length=23B
실제로 위 트래픽이 tls 통신으로 이루어지는지는 "tls=true" 부분을 통해 확인할 수 있다.