본문 바로가기
Service Mesh/Consul

Consul Cluster Peering을 통한 데이터 센터 간 통신

by wlsdn3004 2024. 2. 7.
728x90
반응형

 

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. 공통 설정


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
Information Panel

참고

라이센스가 없으면 주석에 작성한 내용을 확인하여 해당 값을 주석 및 삭제 처리한다.

 

위에서 작성한 "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

 

cluster-01(dc2) UI 대시보드에서 cluster-02(dc3) 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-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 재시작 후 호출테스트 진행하면 된다.

 

반응형

댓글