클러스터를 Cilium으로 마이그레이션하기
클러스터를 Cilium으로 마이그레이션하기
Cilium은 다른 CNI에서 마이그레이션하는 데 사용할 수 있어요. 실행 중인 클러스터를 노드 단위로, 기존 트래픽을 중단하지 않고 마이그레이션할 수 있어요.
본문
Cilium은 다른 CNI에서 마이그레이션하는 데 사용할 수 있어요. 실행 중인 클러스터는 마이그레이션 사례의 복잡성에 따라 전체 클러스터 중단이나 재구축 없이, 기존 트래픽을 방해하지 않으면서 노드 단위로 마이그레이션할 수 있어요.
이 문서는 Cilium으로의 마이그레이션이 어떻게 동작하는지 설명해요. 기본 요구사항을 잘 이해하게 될 것이며, Kind를 사용해 연습할 수 있는 예시 마이그레이션도 보게 될 거예요.
배경
kubelet이 Pod의 Sandbox를 만들 때 /etc/cni/net.d/에 구성된 설치된 CNI가 호출돼요. CNI는 IP 주소 할당, 네트워크 인터페이스 생성·구성, (가능하면) overlay 네트워크 구축 등 pod의 네트워킹을 처리해요. Pod의 네트워크 구성은 PodSandbox와 동일한 수명 주기를 공유해요.
마이그레이션의 경우 일반적으로 /etc/cni/net.d/를 Cilium을 가리키도록 재구성해요. 하지만 기존 Pod는 여전히 이전 네트워크 플러그인이 구성한 상태이고, 새 Pod는 새 CNI가 구성하게 돼요. 마이그레이션을 완료하려면 이전 CNI가 구성한 클러스터의 모든 Pod를 재활용(recycle)해서 새 CNI의 구성원이 되게 해야 해요.
CNI를 마이그레이션하는 단순한 접근 방식은 모든 노드를 새 CNI로 재구성한 뒤 클러스터의 각 노드를 점진적으로 재시작해서 노드가 다시 올라올 때 CNI를 교체하고 모든 pod가 새 CNI의 일부가 되도록 하는 거예요.
이 단순한 마이그레이션은 효과적이긴 하지만, 롤아웃 동안 클러스터 연결을 중단시키는 비용이 들어요. 마이그레이션되지 않은 노드와 마이그레이션된 노드는 두 개의 연결 "섬(island)"으로 분리되어, 마이그레이션이 완료될 때까지 pod들이 서로 무작위로 연결되지 못할 수 있어요.
이중 overlay를 통한 마이그레이션
대신 Cilium은 클러스터 전반에 두 개의 별도 overlay를 구축하는 하이브리드 모드를 지원해요. 특정 노드의 pod는 하나의 네트워크에만 연결될 수 있지만, 마이그레이션이 진행되는 동안 Cilium pod와 non-Cilium pod 모두에 접근할 수 있어요. Cilium과 기존 네트워킹 제공자가 별도의 IP 범위를 사용하는 한, Linux 라우팅 테이블이 트래픽 분리를 처리해요.
이 문서에서는 두 개의 배포된 CNI 구현 사이에서 라이브 마이그레이션하는 모델을 다룰게요. 이렇게 하면 노드와 워크로드의 다운타임을 줄이고, 구성된 두 CNI의 워크로드가 마이그레이션 중에도 통신할 수 있게 보장할 수 있어요.
라이브 마이그레이션이 동작하려면 Cilium을 현재 설치된 CNI와 다른 별도의 CIDR 범위와 캡슐화 포트로 설치해요. Cilium과 기존 CNI가 별도의 IP 범위를 사용하는 한, Linux 라우팅 테이블이 트래픽 분리를 처리해요.
요구사항
라이브 마이그레이션에는 다음이 필요해요.
- Cilium이 사용할 새롭고 별개의 Cluster CIDR
- Cluster Pool IPAM mode 사용
- 프로토콜 또는 포트가 다른 별개의 overlay
- Flannel, Calico, AWS-CNI 같은 Linux 라우팅 스택을 사용하는 기존 네트워크 플러그인
제한사항
현재 Cilium 마이그레이션은 다음과 함께 테스트되지 않았어요.
- BGP 기반 라우팅
- IP 패밀리 변경(예: IPv4에서 IPv6로)
- 체이닝 모드의 Cilium에서 마이그레이션
- 기존 NetworkPolicy 제공자
마이그레이션 중에는 Cilium의 NetworkPolicy와 CiliumNetworkPolicy enforcement가 비활성화돼요. 그렇지 않으면 non-Cilium pod의 트래픽이 잘못 드롭될 수 있어요. 마이그레이션 프로세스가 완료되면 정책 enforcement를 다시 활성화할 수 있어요. 기존 NetworkPolicy 제공자가 있다면 진행 전에 모든 NetworkPolicy를 임시로 삭제하는 것이 좋아요.
Cilium을 cluster-pool IPAM 할당자로 설치하는 것을 강력히 권장해요. 이렇게 하면 IP 충돌이 없을 것이라는 가장 강력한 보장을 얻을 수 있어요.
경고
마이그레이션은 기존 클러스터의 정확한 구성에 크게 의존해요. 따라서 테스트 또는 실습 클러스터에서 시험 마이그레이션을 수행하는 것을 강력히 권장해요.
개요
마이그레이션 프로세스는 per-node configuration 기능을 사용해 Cilium CNI를 선택적으로 활성화해요. 이렇게 하면 기존 워크로드를 방해하지 않고 Cilium을 통제된 방식으로 롤아웃할 수 있어요.
Cilium은 먼저 overlay를 구축하지만 어떤 pod에도 CNI 네트워킹을 제공하지 않는 모드로 설치돼요. 그런 다음 개별 노드가 마이그레이션돼요.
요약하면 프로세스는 다음과 같아요.
- "secondary" 모드로 cilium 설치
- 각 노드에 cordon, drain, migrate, reboot
- 기존 네트워크 제공자 제거
- (선택) 각 노드를 다시 reboot
마이그레이션 절차
준비
$ cat <<EOF > kind-config.yaml
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
nodes:
- role: control-plane
- role: worker
- role: worker
networking:
disableDefaultCNI: true
EOF
$ kind create cluster --config=kind-config.yaml
$ kubectl apply -n kube-system --server-side -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/misc/migration/install-reference-cni-plugins.yaml
$ kubectl apply --server-side -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
$ kubectl wait --for=condition=Ready nodes --all
- 선택사항: 연결성을 모니터링해요. 가능한 연결 문제를 감지하기 위해 goldpinger 같은 도구를 설치해도 좋아요.
-
pod를 위한 새 CIDR을 선택해요. 사용 중인 다른 모든 CIDR과 달라야 해요. Kind 클러스터의 기본값은
10.244.0.0/16이에요. 그래서 이 예시에서는10.245.0.0/16을 사용할게요. -
별개의 캡슐화 포트를 선택해요. 예를 들어 기존 클러스터가 VXLAN을 사용한다면 GENEVE를 쓰거나 Cilium이 다른 포트로 VXLAN을 사용하도록 구성해야 해요. 이 예시에서는 기본이 아닌 포트 8473의 VXLAN을 사용할게요.
-
다음 예시를 바탕으로 helm
values-migration.yaml파일을 만들어요. 1단계에서 선택한 CIDR을 반드시 채워 넣으세요.
operator:
unmanagedPodWatcher:
restart: false # Migration: Don't restart unmigrated pods
routingMode: tunnel # Migration: Optional: default is tunneling, configure as needed
tunnelProtocol: vxlan # Migration: Optional: default is VXLAN, configure as needed
tunnelPort: 8473 # Migration: Optional, change only if both networks use the same port by default
cni:
customConf: true # Migration: Don't install a CNI configuration file
uninstall: false # Migration: Don't remove CNI configuration on shutdown
ipam:
mode: "cluster-pool"
operator:
clusterPoolIPv4PodCIDRList: ["10.245.0.0/16"] # Migration: Ensure this is distinct and unused
policyEnforcementMode: "never" # Migration: Disable policy enforcement
bpf:
hostLegacyRouting: true # Migration: Allow for routing between Cilium and the existing overlay
- 추가 Cilium Helm values를 구성해요. Cilium은 많은 Helm 구성 옵션을 지원해요. cilium-cli를 사용해 일반적인 옵션들을 자동 감지할 수 있어요. 이렇게 하면 템플릿을 소비하고 관련된 다른 Helm values를 자동 감지해요. 특정 설치에 맞게 이 값들을 검토하세요.
$ cilium install 1.20.2 --values values-migration.yaml --dry-run-helm-values > values-initial.yaml
$ cat values-initial.yaml
- helm을 사용해 cilium을 설치해요.
$ helm repo add cilium https://helm.cilium.io/
$ helm install cilium cilium/cilium --namespace kube-system --values values-initial.yaml
이 시점에서 Cilium이 설치되고 overlay가 구축된 클러스터가 되지만, Cilium이 관리하는 pod는 없어요. cilium 명령으로 확인할 수 있어요.
$ cilium status --wait
...
Cluster Pods: 0/3 managed by Cilium
- 노드에서 CNI 네트워킹을 Cilium이 "인수(take over)"하도록 지시하는 per-node config를 생성해요. 처음에는 어떤 노드에도 적용되지 않으며, 마이그레이션 프로세스를 통해 점진적으로 롤아웃할 거예요.
cat <<EOF | kubectl apply --server-side -f -
apiVersion: cilium.io/v2
kind: CiliumNodeConfig
metadata:
namespace: kube-system
name: cilium-default
spec:
nodeSelector:
matchLabels:
io.cilium.migration/cilium-default: "true"
defaults:
write-cni-conf-when-ready: /host/etc/cni/net.d/05-cilium.conflist
custom-cni-conf: "false"
cni-chaining-mode: "none"
cni-exclusive: "true"
EOF
마이그레이션
이 시점에서 마이그레이션 프로세스를 시작할 준비가 됐어요. 기본 흐름은 다음과 같아요.
마이그레이션할 노드를 선택해요. 컨트롤 플레인 노드부터 시작하는 것은 권장하지 않아요.
$ NODE="kind-worker" # for the Kind example
- 해당 노드를 cordon하고, 선택적으로 drain 해요.
$ kubectl cordon $NODE
$ kubectl drain --ignore-daemonsets $NODE
Drain은 엄격히 필수는 아니지만 권장돼요. 그렇지 않으면 노드를 재부팅하는 동안 pod가 잠깐 중단을 겪을 거예요.
- 노드에 라벨을 붙여요. 그러면 이 노드에
CiliumNodeConfig가 적용돼요.
$ kubectl label node $NODE --overwrite "io.cilium.migration/cilium-default=true"
- Cilium을 재시작해요. 그러면 CNI 구성 파일을 쓰게 돼요.
$ kubectl -n kube-system delete pod --field-selector spec.nodeName=$NODE -l k8s-app=cilium
$ kubectl -n kube-system rollout status ds/cilium -w
- 노드를 재부팅해요. kind를 사용한다면 docker로 해요.
docker restart $NODE
- 노드가 성공적으로 마이그레이션됐는지 검증해요.
$ cilium status --wait
$ kubectl get -o wide node $NODE
$ kubectl -n kube-system run --attach --rm --restart=Never verify-network \
--overrides='{"spec": {"nodeName": "'$NODE'", "tolerations": [{"operator": "Exists"}]}}' \
--image ghcr.io/nicolaka/netshoot:v0.8 -- /bin/bash -c 'ip -br addr && curl -s -k https://$KUBERNETES_SERVICE_HOST/healthz && echo'
pod의 IP 주소가 위에서 제공한 Cilium CIDR에 있는지, apiserver에 접근 가능한지 확인하세요.
- 노드를 uncordon 해요.
$ kubectl uncordon $NODE
모든 것이 성공적으로 마이그레이션됐다고 판단되면 클러스터에서 마이그레이션되지 않은 다른 노드를 선택해 이 단계들을 반복해요.
마이그레이션 후
클러스터가 완전히 마이그레이션된 후 이 단계들을 수행해요.
- Cilium이 정상이고 모든 pod가 마이그레이션됐는지 확인해요.
$ cilium status
- Cilium 구성을 업데이트해요. Cilium이 기본 CNI여야 하고 NetworkPolicy가 적용되어야 하며 Operator가 unmanaged pod를 재시작할 수 있어야 해요. 선택사항: eBPF Host-Routing 사용. 이를 활성화하면 데몬이 재시작되면서 각 노드에서 짧은 연결 중단이 발생하지만 네트워킹 성능이 향상돼요. 수동으로 하거나
cilium도구로 할 수 있어요(클러스터에는 변경을 적용하지 않음).
$ cilium install 1.20.2 --values values-initial.yaml --dry-run-helm-values \
--set operator.unmanagedPodWatcher.restart=true --set cni.customConf=false \
--set policyEnforcementMode=default \
--set bpf.hostLegacyRouting=false > values-final.yaml # optional, can cause brief interruptions
$ diff values-initial.yaml values-final.yaml
그런 다음 클러스터에 변경 사항을 적용해요.
$ helm upgrade --namespace kube-system cilium cilium/cilium --values values-final.yaml
$ kubectl -n kube-system rollout restart daemonset cilium
$ cilium status --wait
- per-node 구성을 삭제해요.
$ kubectl delete -n kube-system ciliumnodeconfig cilium-default
- 이전 네트워크 플러그인을 삭제해요. 이 시점에서는 모든 pod가 네트워킹에 Cilium을 사용해야 해요.
cilium status로 쉽게 확인할 수 있어요. 이제 클러스터에서 이전 네트워크 플러그인을 삭제해도 안전해요. 대부분의 네트워크 플러그인은 iptables 규칙과 인터페이스 같은 일부 리소스를 남겨요. 이는 노드가 다음에 재부팅될 때 정리돼요. 원한다면 롤링 재부팅을 다시 수행할 수 있어요.