Cilium ClusterMesh 구성해보기

 

멀티 클러스터 환경에서 다양하게 구성하는 경우가 많아서 이번에는 Cilium의 ClusterMesh를 이용하여 Mesh환경을 구성해보도록 하겠습니다.

 

 

클러스터 구성 전 주의사항

  • 설치 전 주의사항은 두개의 클러스터가 모두 같은 네트워크 모드를 사용해야합니다. 한쪽이 vxlan 캡슐화를 하는데 한쪽은 native routing을 사용하면 안됩니다.
  • 각 클러스터의 pod, service cidr은 겹치면 안됩니다. 
  • 모든 클러스터의 노드는 서로 IP 연결이 가능해야 하며, 이는 각 노드의 InternalIP를 사용하여 구성됩니다. 이 요구 사항은 일반적으로 클러스터 간 네트워크 피어링 또는 VPN 터널을 설정하여 충족됩니다.
  • 클러스터 간의 네트워크는 상호 통신을 허용해야 하며, 구체적인 포트는 방화벽 규칙(Firewall Rules) 섹션에서 확인할 수 있습니다.
  • 만약 네이티브 라우팅방식으로 구성한다면 ipv4-native-routing-cidr는 클러스터 간의 Pod 통신을 가능하게 하기 위해 모든 연결된 클러스터의 PodCIDR 범위를 포함하는 CIDR을 설정해야 합니다.
  • Mesh 추가할수 있는 기본은 255이며 변경 가능한데 수정할 있는 최대의 클러스터 수는 511입니다.

 

Cluster 2개 준비하기

구분하기 쉽게 파드와 서비스 대역을 나누어서 클러스터를 2개 생성하겠습니다.

Blue 클러스터 만들기

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
  - role: worker
  - role: worker
  - role: worker

networking:
  disableDefaultCNI: true
  kubeProxyMode: "none"
  podSubnet: "100.10.0.0/16"
  serviceSubnet: "100.11.0.0/16"

 

위의 파일을 이용하여 클러스터를 생성합니다.

kind create cluster --name blue --config blue/kind.yaml

 

생성 완료

 

Green 클러스터 만들기

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
  - role: worker
  - role: worker
  - role: worker

networking:
  disableDefaultCNI: true
  kubeProxyMode: "none"
  podSubnet: "200.10.0.0/16"
  serviceSubnet: "200.11.0.0/16"

 

위의 파일을 이용하여 클러스터를 생성합니다.

kind create cluster --name green --config green/kind.yaml

 

생성 완료

 

Cilium 설치하기

아래의 명령어를 사용해서 설치할 예정입니다.

cluster id는 0번은 cluster mesh를 안사용하는 클러스터에 적용할 때 두면 좋을 것 같고 cluster-mesh로 묶을 클러스터에는 1번부터 순서대로 적용하면 될 것 같습니다. 

cilium install --version 1.16.2 --context kind-${CLUSTER_NAME} \
    --set kubeProxyReplacement=true \
    --set gatewayAPI.enabled=true \
    --set hubble.enabled=true \
    --set hubble.relay.enabled=true \
    --set hubble.ui.enabled=true \
    --set l2announcements.enabled=true \
    --set k8sServiceHost=${KUBE_API_SERVER_IP} \
    --set k8sServicePort=${KUBE_API_SERVER_PORT} \
    --set externalIPs.enabled=true \
    --set cluster.name=${CLUSTER_NAME} \
    --set cluster.id=${CLUSTER_ID}

 

이 스크립트로 설치하면 빠르게 설치할 수 있습니다.

#!/bin/bash
clusters=("blue" "green")
index=0
for cluster in "${clusters[@]}"
do
    # index 값을 1씩 증가
    index=$((index + 1))

    # 환경 변수 설정
    export CONTEXT="kind-$cluster"
    export CLUSTER_NAME=$cluster
    export CONTROLPLANE_NAME="$cluster-control-plane"

    # kubectl을 사용하여 KUBE_API_SERVER_IP 가져오기
    export KUBE_API_SERVER_IP=$(kubectl get nodes $CONTROLPLANE_NAME -o json --context $CONTEXT | jq '.status.addresses[0].address' -r)

    # API 서버 포트 설정
    export KUBE_API_SERVER_PORT=6443
    export CLUSTER_ID=$index

    # 값 출력
    echo "Context: $CONTEXT"
    echo "Cluster Name: $CLUSTER_NAME"
    echo "Kube API Server IP: $KUBE_API_SERVER_IP"
    echo "Cluster ID: $CLUSTER_ID"
    echo "----------------------------"

    cilium install --version 1.16.2 --context ${CONTEXT} \
        --set kubeProxyReplacement=true \
        --set gatewayAPI.enabled=true \
        --set hubble.enabled=true \
        --set hubble.relay.enabled=true \
        --set hubble.ui.enabled=true \
        --set l2announcements.enabled=true \
        --set k8sServiceHost=${KUBE_API_SERVER_IP} \
        --set k8sServicePort=${KUBE_API_SERVER_PORT} \
        --set externalIPs.enabled=true \
        --set cluster.name=${CLUSTER_NAME} \
        --set cluster.id=${CLUSTER_ID}
done

 

l2 설정의 경우 두 클러스터에 동일하게 설정하면 됩니다.

apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
  name: default
spec:
  externalIPs: true
  loadBalancerIPs: true

 

ipam 설정하기

blue 클러스터에 다음과 같은 ipam을 등록하겠습니다.

apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: ipam-blue
spec:
  blocks:
    - start: 172.17.0.51
      stop: 172.17.0.100

 

green클러스터에는 헷갈리지 않도록 다른 ipam을 등록하겠습니다.

apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: ipam-green
spec:
  blocks:
    - start: 172.17.1.101
      stop: 172.17.1.150

 

Cluster Mesh 활성화하기

설정 활성화하기

cilium clustermesh enable --context $CONTEXT --service-type NodePort

 

설정이 되면 cluster-mesh 전용 apiserver가 생성 된다.

 

클러스터 끼리 연결하기

istio에서는 mesh로 연결하려고 할때 양쪽에 설정을 해줘야했는데 여기는 이상하게도 한곳에 설정했는데 양쪽다 설정이 들어간것 같다.

cilium clustermesh connect --context $CLUSTER1 --destination-context $CLUSTER2

 

아래의 명령어로 정상적으로 설정되었는지 확인해볼 수 있다. 

cilium clustermesh status

 

테스트 해보기

테스트 서비스 생성하기

양쪽에 동일하게 생성하게 합니다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  annotations:
    service.cilium.io/global: "true"
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 8080
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/kong/httpbin:0.1.0
        imagePullPolicy: IfNotPresent
        name: httpbin
        # Same as found in Dockerfile's CMD but using an unprivileged port
        command:
        - pipenv
        - run
        - gunicorn
        - -b
        - 0.0.0.0:8080
        - httpbin:app
        - -k
        - gevent
        env:
        # Tells pipenv to use a writable directory instead of $HOME
        - name: WORKON_HOME
          value: /tmp
        ports:
        - containerPort: 8080

 

Global 설정

이렇게 설정하고 나면 양쪽의 서비스의 엔드포인트에 다른 쪽의 파드들까지 들어오게 됩니다.

 

blue 클러스터의 cilium에서 서비스 리스트를 확인해보면 my-service에 green 클러스터의 파드들이 보이는것을 확인할 수 있습니다.

 

 

좀더 확인하기 쉽게 응답을 다르게 주게끔 했는데 이렇게 아이피로 호출했을 때에도 잘 응답이 오는것을 확인할 수 있다.

 

 

hubble에도 신기하게 두개의 클러스터가보인다.