Standalone CIS Default Mode – BIG-IP CIS Multi-Cluster 구성
Standalone CIS(Container Ingress Services)는 단일 CIS 인스턴스로 여러 Kubernetes/OpenShift 클러스터를 하나의 BIG-IP에서 통합 관리할 수 있는 Multi-Cluster 구성 방식입니다.
동일한 목적의 클러스터를 BIG-IP 레벨에서 통합함으로써, 클러스터 단위의 고가용성 구성을 구현할 수 있습니다.
이번 포스트에서는 서로 다른 버전의 OpenShift 클러스터 2개를 대상으로 Standalone CIS를 Default Mode로 구성하고, 두 클러스터에 Pod를 배포한 후 CIS의 VirtualServer 리소스를 배포하여 BIG-IP Pool Member로 정상 등록되는지 확인하는 과정을 살펴보겠습니다.
이번 포스트의 내용은 BIG-IP CIS의 Static Route를 통한 ClusterIP 모드로 구성되었습니다.
목차
1. Standalone CIS Default Mode란?
1-1. 동작 방식
2. 환경/버전 정보
3. Standalone CIS 구성 전 사전 준비
3-1. 보조 클러스터 접근을 위한 kubeconfig Secret 생성
3-2. Default Mode Extended ConfigMap 생성
4. Standalone CIS – Default Mode 배포
5. VirtualServer 구성 및 BIG-IP Pool 확인
6. 결론
1. Standalone CIS Default Mode란?

Standalone CIS Default Mode는 단일 CIS 인스턴스가 여러 Kubernetes/OpenShift 클러스터를 관리하는 Multi-Cluster 구성 방식입니다. CIS가 배포된 로컬 클러스터뿐만 아니라, kubeconfig를 통해 연결된 외부 클러스터의 리소스도 함께 조회하여 BIG-IP Pool Member로 등록합니다.
CIS의 Multi-Cluster 구성 방식은 크게 두 가지로 나뉩니다. Standalone CIS는 단일 CIS 인스턴스가 여러 클러스터를 관리하는 방식이며, High Availability CIS는 여러 클러스터 중 두 클러스터에 CIS 인스턴스를 배포하여 이중화된 CIS 구성을 갖추는 방식입니다.
또한 Multi-Cluster 구성에서는 트래픽 분배 방식에 따라 Default Mode와 Ratio Mode를 선택할 수 있습니다. Default Mode는 모든 클러스터의 Pool Member를 동등하게 등록하여 BIG-IP의 기본 로드밸런싱 알고리즘에 따라 트래픽을 분배하는 방식입니다. Ratio Mode는 클러스터 별로 가중치를 지정하여 트래픽 비율을 조정할 수 있는 방식으로, 클러스터 간 점진적 트래픽 전환이 필요한 경우에 활용됩니다.
이번 포스트에서는 Standalone CIS를 Default Mode로 구성하여, 서로 다른 버전의 OCP 클러스터 2개의 Pool Member가 BIG-IP에 정상적으로 등록되는지 확인해 보겠습니다.
1-1. 동작 방식
Standalone CIS Default Mode는 다음과 같은 방식으로 동작합니다.
- CIS는 로컬 클러스터(1번 클러스터)에 Pod 형태로 배포되며,
multi_cluster_mode: standalone옵션으로 Multi-Cluster, standalone 모드로 동작합니다. - Extended ConfigMap에 정의된 kubeconfig Secret을 통해 외부 클러스터 정보를 참조하여, 2번 클러스터의 리소스를 함께 조회합니다.
- VirtualServer 등의 CIS Custom Resource에
multiClusterServices옵션으로 외부 클러스터의 Service를 지정하면, CIS는 로컬 및 외부 클러스터의 Pod(혹은 Node)를 모두 BIG-IP Pool Member로 등록합니다. - 최종적으로 BIG-IP는 등록된 Pool Member를 대상으로 트래픽을 분배합니다.
2. 환경/버전 정보
| 구성 요소 | 버전 |
|---|---|
| Red Hat OpenShift Cluster 1 | 4.18.24 |
| Red Hat OpenShift Cluster 2 | 4.21.6 |
| CNI | OVN-Kubernetes |
| F5 BIG-IP VE | 17.5.0 |
| F5 Container Ingress Services Operator | v1.21.0 |
| F5 BIG-IP CIS | 2.20.3 |
| AS3(Application Services 3) Extension | v3.56.0 |
3. Standalone CIS 구성 전 사전 준비
Standalone CIS Default Mode 구성 전에, BIG-IP CIS Multi-Cluster 환경에 필요한 사전 준비를 진행합니다.
주의:
1번 클러스터의 노드에서 2번 클러스터 API 서버(포트 6443)로의 네트워크 접근이 허용되어 있어야 합니다. 방화벽 또는 보안 그룹 정책에서 해당 통신이 차단되어 있으면 CIS가 외부 클러스터 리소스를 조회할 수 없습니다.
3-1. 보조 클러스터 접근을 위한 kubeconfig Secret 생성
Standalone CIS는 1번 클러스터에 배포되어 2번 클러스터의 리소스를 함께 조회합니다. 이를 위해 2번 클러스터에 CIS 전용 ServiceAccount와 권한을 생성하고, 해당 권한(토큰)으로 kubeconfig 파일을 만들어 1번 클러스터에 Secret으로 등록하는 과정을 진행합니다.
다음 yaml 파일을 사용하여, 2번 클러스터의 kube-system 네임스페이스에 bigip-ctlr Service Account를 생성하고, nodes, services, endpoints, namespaces, pods 리소스에 대해 get, list, watch 권한을 부여합니다.
# for reference only# Should be changed as per your cluster requirementskind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: bigip-ctlr-clusterrolerules:- apiGroups: [""] resources: ["nodes", "services", "endpoints", "namespaces", "pods"] verbs: ["get", "list", "watch"]kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: bigip-ctlr-clusterrole-binding namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: bigip-ctlr-clusterrolesubjects: - apiGroup: "" kind: ServiceAccount name: bigip-ctlr namespace: kube-systemapiVersion: v1kind: ServiceAccountmetadata: name: bigip-ctlr namespace: kube-system
$ oc apply -f external-cluster-rbac.yaml clusterrole.rbac.authorization.k8s.io/bigip-ctlr-clusterrole createdclusterrolebinding.rbac.authorization.k8s.io/bigip-ctlr-clusterrole-binding createdserviceaccount/bigip-ctlr created
다음 yaml 파일을 사용하여 앞서 생성한 bigip-ctlr Service Account와 연결된 토큰을 생성합니다.
apiVersion: v1kind: Secretmetadata: name: bigip-ctlr-token namespace: kube-system annotations: kubernetes.io/service-account.name: bigip-ctlrtype: kubernetes.io/service-account-token
$ oc apply -f token-secret.yaml secret/bigip-ctlr-token created
다음 스크립트를 사용하여 생성한 토큰으로부터 kubeconfig 파일을 생성합니다.
# 변수 설정SA_NAME="bigip-ctlr"NAMESPACE="kube-system"CLUSTER_NAME="cluster1-2" # 2번 클러스터 이름FILE_NAME="${CLUSTER_NAME}-kubeconfig.yaml"# API 서버 주소 추출 (현재 컨텍스트 기준)SERVER_URL=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')# CA 인증서 추출 (Base64 인코딩 상태 그대로 사용)CA_CERT=$(kubectl get secret ${SA_NAME}-token -n ${NAMESPACE} -o jsonpath='{.data.ca\.crt}')# SA 토큰 추출 (Base64 디코딩하여 사용)TOKEN=$(kubectl get secret ${SA_NAME}-token -n ${NAMESPACE} -o jsonpath='{.data.token}' | base64 --decode)# kubeconfig 파일 생성cat <<EOF > ${FILE_NAME}apiVersion: v1kind: Configclusters:- name: ${CLUSTER_NAME} cluster: certificate-authority-data: ${CA_CERT} server: ${SERVER_URL}contexts:- name: f5-cis-context context: cluster: ${CLUSTER_NAME} namespace: ${NAMESPACE} user: ${SA_NAME}current-context: f5-cis-contextusers:- name: ${SA_NAME} user: token: ${TOKEN}EOFecho "${FILE_NAME} 파일이 성공적으로 생성되었습니다."
$ ./create_kubeconfig.sh cluster1-2-kubeconfig.yaml 파일이 성공적으로 생성되었습니다.
생성한 2번 클러스터의 kubeconfig 파일을 사용하여, 1번 클러스터에서 secret을 kube-system 네임스페이스에 생성합니다.
앞서 스크립트로 생성한 파일의 이름이 cluster1-2-kubeconfig.yaml 이므로 --from-file=kubeconfig=값에 파일 이름을 입력합니다.
$ oc create secret generic kubeconfig-cluster1-2 -n kube-system --from-file=kubeconfig=cluster1-2-kubeconfig.yamlsecret/kubeconfig-cluster1-2 created
3-2. Default Mode Extended ConfigMap 생성
BIG-IP CIS Multi-Cluster 구성에서 참조할 다른 클러스터의 정보를 정의하는 ConfigMap을 생성합니다.
이전 과정에서 kubeconfig 파일로 생성한 secret을 참조합니다.
apiVersion: v1kind: ConfigMapmetadata: name: extended-spec-config namespace: kube-systemdata: extendedSpec: | mode: default externalClustersConfig: - clusterName: cluster1-2 secret: kube-system/kubeconfig-cluster1-2
- mode : Multi-Cluster 구성에서 사용할 모드를 정의합니다. Standalone CIS Default Mode이므로 default를 사용합니다.
- clusterName: CIS가 조회할 보조 클러스터의 이름을 정의합니다.
- secret: 해당하는 보조 클러스터의 kubeconfig로 생성한 secret의 네임스페이스/이름 을 지정합니다.
$ oc apply -f extended-config-default.yamlconfigmap/extended-spec-config configured
4. Standalone CIS – Default Mode 배포
1번 클러스터에 Standalone CIS를 Default Mode로 배포합니다.
이 포스트에서는 OpenShift 클러스터에 설치된 F5 Container Ingress Services Operator를 사용하여 CIS를 배포했습니다. Static Route를 활용한 ClusterIP 모드로 구성했으며, NodePort 모드도 동일하게 구성 가능합니다.
kind: F5BigIpCtlrapiVersion: cis.f5.com/v1metadata: name: f5bigipctlr namespace: openshift-operatorsspec: args: agent: as3 bigip_partition: ocp-cluster-1 # CIS 연동할 BIG-IP 파티션 bigip_url: 192.168.40.121 # BIG-IP VE IP insecure: true log_as3_response: true log_level: INFO manage_routes: false manage_ingress: false custom_resource_mode: true # Custom Resource 사용 모드 설정 ### OpenShift OVN-K8s 사용 clusterIP, static routing 설정 pool_member_type: cluster static_routing_mode: true orchestration_cni: ovn-k8s ### CIS Multi-Cluster 설정 multi_cluster_mode: standalone # Standalone CIS 설정 local_cluster_name: cluster1-1 # CIS 배포 클러스터 이름 설정 extended_spec_configmap: kube-system/extended-spec-config # 앞서 생성한 Extended ConfigMap의 네임스페이스/이름 bigip_login_secret: f5-bigip-ctlr-login # BIG-IP 로그인 정보가 기록된 secret image: pullPolicy: Always repo: k8s-bigip-ctlr user: f5networks ingressClass: create: false defaultController: false ingressClassName: f5 namespace: kube-system rbac: create: true resources: {} serviceAccount: create: true version: latest
Helm 활용 배포 시에도 동일한 옵션을 사용 가능하나, 일부 옵션의 값에 차이가 있어 확인이 필요합니다.
예: F5BigIpCtlr(Operator): custom_resource_mode / Helm: custom-resource-mode
Helm value 전체 옵션은 링크에서 확인하세요.
배포가 완료된 후 CIS Pod의 로그를 조회하면, Multi-Cluster 모드, standalone CIS 로 동작 중인 것을 확인할 수 있습니다. 또한 custom resource mode로 동작하여 관련 informer의 시작 상태도 확인할 수 있습니다.
$ oc get po -n kube-systemNAME READY STATUS RESTARTS AGEf5bigipctlr-f5-bigip-ctlr-59dcd47b66-5f9h8 1/1 Running 0 2m39s------$ oc logs f5bigipctlr-f5-bigip-ctlr-59dcd47b66-5f9h8 -n kube-system2026/04/08 05:35:40 [INFO] [MultiCluster] CIS running with multi-cluster-mode: standalone2026/04/08 05:35:40 [INFO] [INIT] Starting: Container Ingress Services - Version: v2.20.3, BuildInfo: gitlab-16324349-acadb9e63a1117defbb70d6096950df1fe5db713......2026/04/08 05:36:04 [INFO] Starting VirtualServer Informer2026/04/08 05:36:04 [INFO] Starting TLSProfile Informer2026/04/08 05:36:04 [INFO] Starting TransportServer Informer2026/04/08 05:36:04 [INFO] Starting IngressLink InformerI0408 05:36:04.732975 1 shared_informer.go:240] Waiting for caches to sync for F5 CIS Ingress ControllerI0408 05:36:05.133512 1 shared_informer.go:247] Caches are synced for F5 CIS Ingress Controller2026/04/08 05:36:05 [WARNING] Ensure Global Extended Configmap is created in CIS monitored namespace2026/04/08 05:36:05 [INFO] Using cluster-specific CIS identifier: 192.168.40.121_cluster1-1 (cluster: cluster1-1, nodeLabelSelector: )
5. VirtualServer 구성 및 BIG-IP Pool 확인
각 클러스터에 동일한 애플리케이션의 Deployment/Service를 배포하고, CIS Custom Resource인 VirtualServer를 구성하여 각 클러스터의 Pod가 BIG-IP Pool로 구성되는 것을 확인하겠습니다.
백엔드 Pod를 생성하고 연결하기 위해 Deployment와 Service를 1번, 2번 클러스터 모두 배포합니다.
default 네임스페이스에 배포했습니다.
apiVersion: apps/v1kind: Deploymentmetadata: name: coffee namespace: defaultspec: replicas: 2 selector: matchLabels: app: coffee template: metadata: labels: app: coffee spec: containers: - name: coffee image: nginxdemos/hello ports: - containerPort: 80
apiVersion: v1kind: Servicemetadata: name: coffee-svc namespace: default labels: app: coffee-svcspec: ports: - name: coffee-svc port: 80 protocol: TCP targetPort: 80 selector: app: coffee
## cluster 1$ oc get po,svc -n default -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/coffee-56b689cd7-d2x6f 1/1 Running 0 78s 10.128.0.24 c1-1-node <none> <none> pod/coffee-56b689cd7-x9ctv 1/1 Running 0 110s 10.128.0.23 c1-1-node <none> <none> NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTORservice/coffee-svc ClusterIP 172.30.177.236 <none> 80/TCP 110s app=coffee service/kubernetes ClusterIP 172.30.0.1 <none> 443/TCP 7d22h <none>service/openshift ExternalName <none> kubernetes.default.svc.cluster.local <none> 7d22h <none>------## cluster 2 $ oc get po,svc -n default -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/coffee-86f468ccd6-kzv6n 1/1 Running 0 35s 10.128.129.97 c1-2-node <none> <none> pod/coffee-86f468ccd6-w2889 1/1 Running 0 48s 10.128.129.96 c1-2-node <none> <none> NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTORservice/coffee-svc ClusterIP 172.30.121.220 <none> 80/TCP 38s app=coffee service/kubernetes ClusterIP 172.30.0.1 <none> 443/TCP 7d5h <none>service/openshift ExternalName <none> kubernetes.default.svc.cluster.local <none> 7d4h <none>
배포한 Pod로 트래픽을 전달하기 위한 CIS의 VirtualServer 리소스를 CIS Pod가 배포된 1번 클러스터에 배포합니다.
동일하게 default 네임스페이스에 배포합니다.
apiVersion: "cis.f5.com/v1"kind: VirtualServermetadata: name: cafe-coffee-vs namespace: default # CIS가 관리할 VirtualServer 리소스임을 식별하는 레이블 labels: f5cr: "true"spec: virtualServerAddress: "192.168.40.230" # BIG-IP에 생성될 VIP virtualServerName: "cafe_coffee_vs" # BIG-IP에 생성될 VirtualServer 이름 host: "cafeone.example.com" pools: - path: "/coffee" # BIG-IP에 구성될 monitor 설정(Health Check) monitor: type: http send: "GET /coffee HTTP/1.1\r\nHost: cafeone.example.com\r\nConnection: Close\r\n\r\n" recv: "" interval: 5 timeout: 16 # 멀티 클러스터 구성에서 트래픽을 전달할 클러스터 이름, 네임스페이스, 서비스 이름 및 포트 정보 multiClusterServices: - clusterName: cluster1-1 # CIS 설정의 local_cluster_name namespace: default service: coffee-svc servicePort: 80 - clusterName: cluster1-2 # Extended ConfigMap에 구성된 clusterName namespace: default service: coffee-svc servicePort: 80
배포가 완료되고, CIS가 정상적으로 처리하면 OK STATUS를 확인할 수 있습니다.
$ oc apply -f cis-vs.yaml virtualserver.cis.f5.com/cafe-coffee-vs created$ oc get virtualservers.cis.f5.com -n default NAME HOST TLSPROFILENAME HTTPTRAFFIC IPADDRESS IPAMLABEL IPAMVSADDRESS STATUS AGEcafe-coffee-vs cafeone.example.com 192.168.40.230 192.168.40.230 OK 73s
CIS Pod의 로그를 확인하면 VirtualServer 구성에 기반한 설정을 BIG-IP로 설정하여 반영하는 것을 확인할 수 있습니다.
$ oc logs f5bigipctlr-f5-bigip-ctlr-59dcd47b66-5f9h8 -n kube-system2026/04/08 07:28:38 [INFO] [Request: 9] cluster cluster1-2 requested CREATE in ENDPOINTS default/coffee-svc2026/04/08 07:28:40 [INFO] [Request: 9][AS3] creating a new AS3 manifest2026/04/08 07:28:40 [INFO] [Request: 9][AS3][BigIP] posting request to https://192.168.40.121 for tenants2026/04/08 07:28:54 [INFO] [Request: 9][AS3][BigIP] post resulted in SUCCESS2026/04/08 07:28:54 [INFO] [AS3][POST] SUCCESS: code: 200 --- tenant:ocp-cluster-1 --- message: success2026/04/08 07:28:54 [INFO] Successfully updated status of VirtualServer:default/cafe-coffee-vs in Cluster cluster1-1
BIG-IP GUI에 접속하여, 좌측 메뉴 바의 Local Traffic > Virtual Servers 메뉴로 이동합니다.

우측 상단의 파티션 지정 메뉴에서 CIS와 연동된 파티션을 선택하면, 클러스터 내부에 배포한 VirtualServers 리소스로 생성된 Virtual Server를 확인할 수 있습니다.

좌측 메뉴 바의 Local Traffic > Pools 메뉴로 이동하면 리스트에서 클러스터 별로 개별 Pool이 구성된 것을 확인할 수 있습니다.


개별 Pool의 이름을 클릭하고, 상단의 Members 버튼을 클릭하면 개별 Pod를 확인할 수 있습니다. CIS를 NodePort 모드로 구성한 경우 노드가 member로 구성됩니다.


## cluster 1$ oc get po -n default -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/coffee-56b689cd7-d2x6f 1/1 Running 0 78s 10.128.0.24 c1-1-node <none> <none> pod/coffee-56b689cd7-x9ctv 1/1 Running 0 110s 10.128.0.23 c1-1-node <none> <none> ------## cluster 2 $ oc get po -n default -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/coffee-86f468ccd6-kzv6n 1/1 Running 0 35s 10.128.129.97 c1-2-node <none> <none> pod/coffee-86f468ccd6-w2889 1/1 Running 0 48s 10.128.129.96 c1-2-node <none> <none>
결과적으로 두 클러스터의 Pod가 각각 Pool Member로 등록되며, BIG-IP는 이를 단일 Virtual Server에서 통합적으로 로드밸런싱하게 됩니다.
6. 결론
이번 포스트에서는 Standalone CIS를 Default Mode로 구성하여, 서로 다른 OpenShift 클러스터의 애플리케이션을 하나의 BIG-IP Virtual Server로 통합하는 방법을 확인했습니다.
보조 클러스터의 kubeconfig Secret 생성부터 Extended ConfigMap 구성, CIS 배포까지의 흐름을 확인했으며, VirtualServer 구성을 통해 각 클러스터 별로 Pool이 등록되는 것을 BIG-IP GUI를 통해 확인했습니다.
BIG-IP CIS와 F5 NGINX Ingress Controller를 활용하여 Kubernetes/OpenShift 환경의 트래픽을 통합 관리하고 싶으시다면 NGINX STORE를 통해 문의하세요.
댓글을 달려면 로그인해야 합니다.