Cluster Peering이란?
Consul에서 Cluster Peering은 독립적인 Consul 서버들을 서로 연결하여, 다양한 파티션과 데이터 센터에 배포된 서비스들이 통신할 수 있게 해주는 기능이다.
아래 그림은 HashiCorp Consul 문서에서 제공하는 Peering 아키텍처이다.
그림 참조: https://developer.hashicorp.com/consul/docs/connect/cluster-peering
위 그림에서는 Consul DC1과 Consul DC2가 Peering 연결 되어있고, 각 DC에 존재하는 Mesh Gateway를 통해 "Service A"에서 "Service D"로 통신할 수 있다는 걸 보여준다.
Cluster Peering의 동작을 보면 Consul의 또 다른 기능인 "WAN Federation"과 유사해 보일 수 있다. 둘 다 데이터 센터 간의 통신을 가능하게 하지만, 중요한 차이점이 있다.
WAN Federation과 Cluster Peering 비교
WAN Federation은 여러 데이터 센터를 연결하여 마치 하나의 큰 클러스터처럼 보이게 한다.
이 구조에서는 주 데이터센터(primary datacenter)가 필요하며, 이 데이터센터는 ACL과 구성 항목에 대해 전역 상태를 유지하고 복제하는 역할을 담당한다. WAN 페더레이션을 사용할 때, Mesh Gateway는 mTLS(상호 TLS)를 사용하여 데이터센터 간에 전송되는 데이터를 암호화하므로, 데이터는 전송 과정에서 계속 암호화 상태를 유지한다.
반면에, Cluster Peering은 각 데이터 센터를 별도의 클러스터로 취급한다.
이 방식에서는 주 데이터센터 (primary datacenter)가 없으며, 각 클러스터는 독립적으로 운영된다. 클러스터 피어링을 사용할 때, 메쉬 게이트웨이는 피어 간의 mTLS 세션을 종료하고, 데이터를 HTTP 서비스로 복호화한 다음, 목적지 서비스로 전송하기 전에 다시 암호화한다. 데이터를 복호화하는 이유는 목적지 클러스터에서 동적 라우팅 규칙을 평가하고 적용하기 위해서다. 이러한 방식은 Peer간의 결합을 줄여준다.
아래 표를 통해 차이를 확인할 수 있다.
WAN Federation | Cluster Peering | |
Connects clusters across datacenters | ✅ | ✅ |
Shares support queries and service endpoints | ✅ | ✅ |
Connects clusters owned by different operators | ❌ | ✅ |
Functions without declaring primary datacenter | ❌ | ✅ |
Can use sameness groups for identical services | ❌ | ✅ |
Replicates exported services for service discovery | ❌ | ✅ |
Gossip protocol: Requires LAN gossip only | ❌ | ✅ |
Forwards service requests for service discovery | ✅ | ❌ |
Shares key/value stores | ✅ | ❌ |
Can replicate ACL tokens, policies, and roles | ✅ | ❌ |
본 글에서 구성할 Cluster Peering 아키텍처는 아래와 같다.
dc2 데이터 센터는 dc3, dc4 데이터 센터와 Peering 연결을 맺은 후 dc2의 static-client 서비스가 dc3, dc4의 static-server를 호출하는 시나리오이다.
구성 환경
- 3개의 Amazon EKS 클러스터 v1.27.7
- Consul v1.17-ent (Enterprise license 이미지 사용)
- Consul Enterprise license (license 없어도 peering 설정 가능)
- Consul Helm Chart 버전 1.3.1
- Helm Cli v3.8.2
- aws-load-balancer-controller v2.6.1
실습 절차
1. 공통 설정2. Cluster-01 설정 (dc2)
3. Cluster-02 설정 (dc3)
4. Cluster-03 설정 (dc4)
5. Peering 연결 후 데이터 센터 간 호출 테스트
1. 공통 설정
Consul Helm repo를 등록한다.
$ helm repo add hashicorp https://helm.releases.hashicorp.com
$ helm repo update
Consul 라이센스가 있으면 라이센스를 적용한다. (없어도 Peering 설정 가능)
# license.yaml
apiVersion: v1
data:
key: {license key base64 incoding}
kind: Secret
metadata:
name: license
namespace: consul
위 라이센스 파일을 적용한다.
$ kubectl apply -f license.yaml
2. Cluster-01 설정 (dc2)
Cluster-01(dc2)의 Helm Chart values 파일을 작성한다.
# consul-dc2-values.yaml
global:
datacenter: dc2
peering:
enabled: true
# 라이센스 없으면 아래 enterpriseLicense 삭제 및 주석
enterpriseLicense:
secretName: license
secretKey: key
# 라이센스 없으면 아래 adminPartitions 삭제 및 주석
adminPartitions:
enabled: true
name: "default"
# 라이센스 없으면 아래 enableConsulNamespaces 삭제 및 주석
enableConsulNamespaces: true
# 라이센스 없으면 아래 이미지 "hashicorp/consul:1.17.1"로 변경
image: hashicorp/consul-enterprise:1.17.1-ent
metrics:
enabled: true
enableAgentMetrics: true
agentMetricsRetentionTime: '1m'
tls:
httpsOnly: false
enabled: true
enableAutoEncrypt: true
verify: false
acls:
manageSystemACLs: true
gossipEncryption:
autoGenerate: true
server:
replicas: 1
exposeGossipAndRPCPorts: true
dns:
enableRedirection: true
enabled: true
connectInject:
enabled: true
# 라이센스 없으면 아래 consulNamespaces 삭제 및 주석
consulNamespaces:
mirroringK8S: true
controller:
enabled: true
ingressGateways:
enabled: true
defaults:
replicas: 1
service:
type: LoadBalancer
annotations: |
"service.beta.kubernetes.io/aws-load-balancer-attributes": load_balancing.cross_zone.enabled=true
"service.beta.kubernetes.io/aws-load-balancer-internal": "true"
"service.beta.kubernetes.io/aws-load-balancer-name": consul-ingress-gateway
"service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": instance
"service.beta.kubernetes.io/aws-load-balancer-scheme": internet-facing
"service.beta.kubernetes.io/aws-load-balancer-subnets": {subnet id}
"service.beta.kubernetes.io/aws-load-balancer-target-group-attributes": stickiness.enabled=false
"service.beta.kubernetes.io/aws-load-balancer-type": external
"service.beta.kubernetes.io/load-balancer-source-ranges": 0.0.0.0/0
terminatingGateways:
enabled: true
defaults:
replicas: 1
meshGateway:
enabled: true
replicas: 1
service:
type: LoadBalancer
annotations: |
"service.beta.kubernetes.io/aws-load-balancer-attributes": load_balancing.cross_zone.enabled=true
"service.beta.kubernetes.io/aws-load-balancer-internal": "true"
"service.beta.kubernetes.io/aws-load-balancer-name": consul-mesh-gateway
"service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": instance
"service.beta.kubernetes.io/aws-load-balancer-scheme": internet-facing
"service.beta.kubernetes.io/aws-load-balancer-subnets": {subnet id}
"service.beta.kubernetes.io/aws-load-balancer-target-group-attributes": stickiness.enabled=false
"service.beta.kubernetes.io/aws-load-balancer-type": external
"service.beta.kubernetes.io/load-balancer-source-ranges": 0.0.0.0/0
참고
라이센스가 없으면 주석에 작성한 내용을 확인하여 해당 값을 주석 및 삭제 처리한다.
위에서 작성한 "consul-dc2-values.yaml" 파일로 Consul을 설치한다.
$ helm upgrade -i -n consul consul -f consul-dc2-values.yaml hashicorp/consul --version 1.3.1 --create-namespace
Consul 설치가 완료되었으면 meshGateway 모드를 local로 설정한다.
# proxydefaults.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ProxyDefaults
metadata:
name: global
spec:
meshGateway:
mode: local
위 CRD 파일을 적용한다.
$ kubectl apply -f proxydefaults.yaml
Mesh Gateway를 통해 Peering 된 control plane 트래픽을 라우팅 하기 위해 아래와 같이 설정한다.
# mesh.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: Mesh
metadata:
name: mesh
spec:
peering:
peerThroughMeshGateways: true
위 CRD 파일을 적용한다.
$ kubectl apply -f mesh.yaml
Peering token을 생성하고, 들어오는 Peering 연결을 수락하기 위해 아래와 같이 설정한다.
두 개를 생성해야 하는데 cluster-02(dc3)와 cluster-03(dc4) 전용 PeeringAcceptor이다.
# acceptor1.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: PeeringAcceptor
metadata:
name: cluster-02
spec:
peer:
secret:
name: "peering-token"
key: "data"
backend: "kubernetes"
# acceptor2.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: PeeringAcceptor
metadata:
name: cluster-03
spec:
peer:
secret:
name: "peering-token2"
key: "data"
backend: "kubernetes"
위 두 개의 CRD 파일을 적용한다.
$ kubectl apply -f acceptor1.yaml -f acceptor2.yaml
위 PeeringAcceptor를 적용하면 "peering-token", "peering-token2" 이름으로 kubernetes secrets이 생성된다.
"peering-token" secret은 cluster-02(dc3)에, "peering-token2" secret은 cluster-03(dc4)에 생성하면 된다.
아래 명령어로 추출하여 보관했다가 추 후 cluster-02, cluster-03 설치 작업 때 적용하면 된다.
$ kubectl get secrets peering-token -oyaml > peering-token.yaml
$ kubectl get secrets peering-token2 -oyaml > peering-token2.yaml
static-client 서비스 yaml파일을 작성한다.
# static-client.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
name: static-client
spec:
protocol: 'http'
---
apiVersion: v1
kind: Service
metadata:
name: static-client
spec:
selector:
app: static-client
ports:
- port: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: static-client
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: static-client
spec:
replicas: 1
selector:
matchLabels:
app: static-client
template:
metadata:
name: static-client
labels:
app: static-client
annotations:
'consul.hashicorp.com/connect-inject': 'true'
spec:
containers:
- name: static-client
image: curlimages/curl:latest
command: ['/bin/sh', '-c', '--']
args: ['while true; do sleep 30; done;']
serviceAccountName: static-client
static-client 서비스를 배포한다.
$ kubectl apply -f static-client.yaml
3. Cluster-02 설정 (dc3)
Cluster-02(dc3)의 Helm Chart values 파일을 작성한다.
- dc3의 values 파일은 dc2의 values 파일과 동일하고 아래 datacenter 값만 변경하면 된다.
# consul-dc3-values.yaml
global:
datacenter: dc3
위에서 작성한 "consul-dc3-values.yaml" 파일로 Consul을 설치한다.
$ helm upgrade -i -n consul consul -f consul-dc3-values.yaml hashicorp/consul --version 1.3.1 --create-namespace
Consul 설치가 완료되었으면 meshGateway 모드를 local로 설정한다.
# proxydefaults.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ProxyDefaults
metadata:
name: global
spec:
meshGateway:
mode: local
위 CRD 파일을 적용한다.
$ kubectl apply -f proxydefaults.yaml
Mesh Gateway를 통해 Peering 된 control plane 트래픽을 라우팅 하기 위해 아래와 같이 설정한다.
# mesh.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: Mesh
metadata:
name: mesh
spec:
peering:
peerThroughMeshGateways: true
위 CRD 파일을 적용한다.
$ kubectl apply -f mesh.yaml
cluster-01(dc2) 설정 때 추출한 secret인 peering-token을 적용한다.
$ kubectl apply -f peering-token.yaml
peering-token을 생성한 cluster-01(dc2)와 아웃바운드 Peering 연결을 맺기 위해 PeeringDialer를 설정한다.
# dialer.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: PeeringDialer
metadata:
name: cluster-01
spec:
peer:
secret:
name: "peering-token"
key: "data"
backend: "kubernetes"
위 CRD 파일을 적용한다.
$ kubectl apply -f dialer.yaml
cluster-02(dc3)의 static-server 서비스를 cluster-01(dc2)에 노출하기 위해 exportedservice를 설정한다.
# exported.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ExportedServices
metadata:
name: default
spec:
services:
- name: static-server
consumers:
- peer: cluster-01
static-server 서비스를 배포하기 위해 yaml파일을 작성한다.
# static-server.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
name: static-server
spec:
protocol: 'http'
---
apiVersion: v1
kind: Service
metadata:
name: static-server
spec:
selector:
app: static-server
ports:
- protocol: TCP
port: 8080
targetPort: 8080
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: static-server
---
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:
consul.hashicorp.com/connect-inject: "true"
spec:
containers:
- name: static-server
image: hashicorp/http-echo:latest
args:
- -text="i'm peer cluster-02"
- -listen=:8080
ports:
- containerPort: 8080
name: http
serviceAccountName: static-server
위 파일을 사용하여 static-server 서비스를 배포한다.
$ kubectl apply -f static-server.yaml
서비스 통신 허용을 위해 serviceintentions를 설정한다.
- 아래 설정은 peering 연결된 cluster-01의 static-client 서비스에서 호출만 허용하겠다는 의미이다.
# intentions.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
name: static-server-intention
spec:
destination:
name: static-server
sources:
- name: "*"
action: deny
- name: static-client
action: allow
peer: cluster-01
4. Cluster-03 설정 (dc4)
Cluster-03(dc4)의 Helm Chart values 파일을 작성한다.
- dc4의 values 파일은 dc2의 values 파일과 동일하고 아래 datacenter 값만 변경하면 된다.
# consul-dc3-values.yaml
global:
datacenter: dc4
위에서 작성한 "consul-dc4-values.yaml" 파일로 Consul을 설치한다.
$ helm upgrade -i -n consul consul -f consul-dc4-values.yaml hashicorp/consul --version 1.3.1 --create-namespace
Consul 설치가 완료되었으면 meshGateway 모드를 local로 설정한다.
# proxydefaults.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ProxyDefaults
metadata:
name: global
spec:
meshGateway:
mode: local
위 ProxyDefaults CRD 파일을 적용한다.
$ kubectl apply -f proxydefaults.yaml
Mesh Gateway를 통해 Peering 된 control plane 트래픽을 라우팅 하기 위해 아래와 같이 설정한다.
# mesh.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: Mesh
metadata:
name: mesh
spec:
peering:
peerThroughMeshGateways: true
위 Mesh CRD 파일을 적용한다.
$ kubectl apply -f mesh.yaml
cluster-01(dc2) 설정 때 추출한 secret인 "peering-token2"을 적용한다.
$ kubectl apply -f peering-token2.yaml
peering-token을 생성한 cluster-01(dc2)와 아웃바운드 Peering 연결을 맺기 위해 PeeringDialer를 설정한다.
# dialer.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: PeeringDialer
metadata:
name: cluster-01
spec:
peer:
secret:
name: "peering-token2"
key: "data"
backend: "kubernetes"
위 CRD 파일을 적용한다.
$ kubectl apply -f dialer.yaml
cluster-03(dc4)의 static-server 서비스를 cluster-01(dc2)에 노출하기 위해 exportedservice를 설정한다.
# exported.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ExportedServices
metadata:
name: default
spec:
services:
- name: static-server
consumers:
- peer: cluster-01
cluster-01(dc2) UI 대시보드에서 cluster-03(dc4) Active 상태를 확인한다.
static-server 서비스를 배포하기 위해 yaml파일을 작성한다.
# static-server.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
name: static-server
spec:
protocol: 'http'
---
apiVersion: v1
kind: Service
metadata:
name: static-server
spec:
selector:
app: static-server
ports:
- protocol: TCP
port: 8080
targetPort: 8080
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: static-server
---
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:
consul.hashicorp.com/connect-inject: "true"
spec:
containers:
- name: static-server
image: hashicorp/http-echo:latest
args:
- -text="i'm peer cluster-03"
- -listen=:8080
ports:
- containerPort: 8080
name: http
serviceAccountName: static-server
위 파일을 사용하여 static-server 서비스를 배포한다.
$ kubectl apply -f static-server.yaml
서비스 통신 허용을 위해 serviceintentions를 설정한다.
- 아래 설정은 peering 연결된 cluster-01의 static-client 서비스에서 호출만 허용하겠다는 의미이다.
# intentions.yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
name: static-server-intention
spec:
destination:
name: static-server
sources:
- name: "*"
action: deny
- name: static-client
action: allow
peer: cluster-01
5. Peering 연결 후 데이터 센터 간 호출 테스트
cluster-01(dc2) Ui 대시보드 Peer목록 부분에서 static-server 서비스가 Imported service로 보이는지 확인한다.
cluster-01(dc2)의 static-client 서비스로 접근하여 각 peering 연결된 클러스터의 static-server를 호출한다.
# static-client 접근
$ kubectl exec -it deploy/static-client -c static-client -- /bin/sh
# cluster-02 static-server 호출
$ curl static-server.virtual.cluster-02.consul
"i'm peer cluster-02"
# cluster-03 static-server 호출
$ curl static-server.virtual.cluster-03.consul
"i'm peer cluster-03"
각 Peering 된 클러스터의 서비스를 정상 호출하는 것을 확인할 수 있다.
[참고]
만약, 연결이 되었다 안되었다 한다면 consul dns 설정을 하면 해결된다.
consul dns 설정은 cluster-01(dc2)에서만 해주면 된다.
아래에서 나오는 consul-dns 서비스의 "CLUSTER-IP"를 Kubernetes coredns configMap 설정에 추가하면 된다.
$ kubectl get svc -n consul consul-consul-dns | awk '{print $3}'
아래 설정 추가
$ kubectl edit cm -n kube-system coredns
# 아래 설정 추가
consul {
errors
cache 30
forward . {consul-dns service ip}
}
위 coredns 적용 후 static-client Pod 재시작 후 호출테스트 진행하면 된다.
'Service Mesh > Consul' 카테고리의 다른 글
Consul 가용 영역 우선순위 라우팅(locality-aware routing) (0) | 2024.02.07 |
---|---|
Consul Peering 환경에서 데이터 센터 간 Failover 구성 (0) | 2024.02.07 |
댓글