[DevOps] kubeadm upgrade를 통한 pod cidr 변경

안녕하세요? 정리하는 개발자 워니즈 입니다. 이번시간에는 중요한 트러블 슈팅을 정리하려고 합니다. 필자가 운영하는 클러스터에는 지속적으로 발생하는 pod cidr 할당 이슈가 있었습니다. 생각해보니 클러스터는 기본적으로 구성할때 셋팅된 cluster cidr와 pod cidr간의 관계를 생각해볼 때, pod도 내부적으로 아이피를 할당 받을텐데 무한정 늘어날 수 있을까? 하는 궁금증이 생겼었습니다.

1. CIDRNotAvailable 메시지 발생

클러스터를 자체 운영하다보면 여러가지 문제들이 발생하게 됩니다. 그 중, 가장 먼저 확인 할 수 있는 것은 쿠버네티스의 이벤트를 확인하는 방법이 있습니다.

$ kubectl get  events -A -w
NAMESPACE     LAST SEEN   TYPE     REASON             OBJECT                                  MESSAGE
default       4m41s       Normal   CIDRNotAvailable   node/kube01                          Node kube01 status is now: CIDRNotAvailable
default       23s         Normal   CIDRNotAvailable   node/kube02                          Node kube02 status is now: CIDRNotAvailable
default       2m56s       Normal   CIDRNotAvailable   node/kube03                          Node kube03 status is now: 

필자는 CIDRNotAvailable 이것에 대해서 정말 궁금증이 많아졌습니다. 어쨋든 CIDR이 부족해졌다는 것이고, 가용한 CIDR가 없으니 할당하는데 문제가 발생했을 것이라고 판단했습니다. 우연치 않게, Calico의 pod들이 간헐적으로 재시작하는 증세가 있었고, 클러스터 자체가 굉장히 불안정했습니다.

실제 IP 할당을 할 때 Controller manager가 담당을 하게 되므로, 해당 로그를 살펴보았습니다.

CIDR allocation failed; there are no remaining CIDRs left to allocate in the accepted range

E0502 03:11:40.819569       1 controller_utils.go:282] Error while processing Node Add/Delete: failed to allocate cidr: CIDR allocation failed; there are no remaining CIDRs left to allocate in the accepted range
I0502 03:11:40.820095       1 event.go:221] Event(v1.ObjectReference{Kind:"Node", Namespace:"", Name:"foo-node", UID:"2e10b7b3-8c14-11ea-a498-066c4ca4071a", APIVersion:"", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'CIDRNotAvailable' Node foo-node status is now: CIDRNotAvailabl

2. Cluster의 CIDR 확인

위의 에러를 확인한 뒤, 클러스터의 CIDR블락이 어떻게 되는지 그리고 각 노드별로 할당이 어떤식으로 되는지 궁금해졌습니다.

클러스터의 CIDR 블록을 확인하려면 마스터 노드에서 다음과 같은 명령을 수행합니다.

# 수행중인 controller-manager의 cluster-cidr 옵션 확인 
pa aux | grep cluster-cidr

# 결과
oot      3159  0.0  0.5 818604 89064 ?        Ssl  May02   1:53 /hyperkube controller-manager --cloud-provider=aws --cluster-name=foo-cluster --kubeconfig=/etc/kubernetes/kubeconfig/kube-controller-manager.yaml --authentication-kubeconfig=/etc/kubernetes/kubeconfig/kube-controller-manager.yaml --authorization-kubeconfig=/etc/kubernetes/kubeconfig/kube-controller-manager.yaml --leader-elect=true --root-ca-file=/etc/kubernetes/ssl/ca.pem --service-account-private-key-file=/etc/kubernetes/ssl/service-account-key.pem --use-service-account-credentials --cluster-signing-cert-file=/etc/kubernetes/ssl/worker-ca.pem --cluster-signing-key-file=/etc/kubernetes/ssl/worker-ca-key.pem --allocate-node-cidrs=true --cluster-cidr=10.2.0.0/18 --configure-cloud-routes=false --service-cluster-ip-range=10.3.0.0/16
core      6843  0.0  0.0   2820   844 pts/0    S+   14:07   0:00 grep --colour=auto cluster-cidr

--cluster-cidr 옵션 값을 확인 합니다.
–cluster-cidr=10.2.0.0/18

클러스터의 CIDR값이 16을 가르키고 있습니다. CIDR는 각 클래스마다 2진수로 표기하여 해당 자리 수 만큼을 변동이 가능한 자리로 보는 것인데요. 이에 대한 내용은 이번 포스팅에는 담지 않겠습니다. 어쨋든 /16의 의미는 10.2.X.X 에서 X(1-64) * X(1-255) 만큼에 대하여 아이피를 할당 할 수 있습니다. 즉, 16,384개의 아이피 할당이 가능합니다.

자, 그러면 클러스터에서는 /16 만큼의 아이피 할당이 가능하다고 합니다. 그럼 개별 노드에 대해서 알아보겠습니다.

노드의 CIDR 블록을 확인하려면 마스터 노드에서 다음 내용을 확인 합니다.

# kubeadm-config 파일 확인 
cat /etc/kubernetes/kubeadm-config.yml

# node cidr 확인 
controllerManager:
  extraArgs:
    node-monitor-grace-period: 40s
    node-monitor-period: 5s
    pod-eviction-timeout: 5m0s
    node-cidr-mask-size: "24"
    profiling: "False"
    terminated-pod-gc-threshold: "12500"
    bind-address: 0.0.0.0
    configure-cloud-routes: "false"

node-cidr-mask-size 옵션 값을 확인 합니다.
Node-cidr-mask-size: “24”

노드의 CIDR값이 24를 가르키고 있습니다. /24의 의미는 10.2.0.X 에서 X(1-255) 만큼에 대하여 아이피를 할당 할 수 있습니다. 즉, 256 개의 POD 아이피 할당이 가능합니다.

자, 그러면 노드에서는 /24만큼의 아이피 할당이 가능하다고 합니다.

Kubernetes의 클러스터에서 허용하는 갯수와 노드 개별적으로 허용하는 갯수가 있으니 전체 노드가 몇대가 가능한지 계산이 됩니다. 즉, 256개의 노드(24-18=6, 2^6=64)가 있을 수 있습니다.

여기서 문제의 원인을 찾았습니다. 필자가 운영하는 클러스터는 64대 이상의 워커노드를 갖고 있습니다. 그렇다면 CIDR 조정이 필요해보이는군요. 바로 CIDR조정을 위해서, kubeadm 업그레이드 를 알아보고 절차에 맞춰서 진행했습니다.

IP 주소 할당 최적화

3. Kubeadm을 통한 업그레이드 절차

kubeadm을 통해서 업그레이드 하는 방식을 확인했습니다. 쿠버네티스는 configmap 형식으로 각 옵션 값들을 갖고 있습니다. 그렇다면 조회해 보도록 하겠습니다.

# kubeadm config 조회
kubectl get cm -n kube-system | grep 'kubeadm'
kubeadm-config     

# kubeadm config describe 조회
kubectl describe cm kubeadm-config -n kube-system
controllerManager:
.
.
    node-cidr-mask-size: "24"
networking:
  dnsDomain: cluster.local
  podSubnet: 10.233.64.0/18
  serviceSubnet: 10.233.0.0/18

위와 같이 cluster cidr는 networking에 표기되는 /18로, node cidr는 controllermanager에 표기되는 /24로 내용이 나와 있습니다.

3-1. kubeadm format으로 config 파일 가져오기

# 현재 구성된 kubeadm config 파일 가져오기 
kubeadm config view > kubeadm-config.yaml

실패의 경험으로 다시 정리를 하는 와중에 알게 된 내용입니다. 여기서 CIDR을 변경하는 것은 절대 권장하지 않습니다. 필자의 경우 kube-controller-manager가 정상적으로 기동되지 못하는 현상이 있었습니다. cidr할당에 문제가 생긴 클러스터에서 controller 자체기동이 되지 않는 문제가 있어, 다시 해결을 해야했습니다.

3-2. config 변경

# vi를 통한 변경
$ vi kubeadm-config.yaml
apiServer:
  extraArgs:
    advertise-address: 192.168.5.102
    authorization-mode: Node,RBAC
    runtime-config: apps/v1beta1=true,apps/v1beta2=true,extensions/v1beta1/daemonsets=true,extensions/v1beta1/deployments=true,extensions/v1beta1/replicasets=true,extensions/v1beta1/networkpolicies=true,extensions/v1beta1/podsecuritypolicies=true
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta2
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: kube-lb:6443
controllerManager: {}
dns:
  type: CoreDNS
etcd:
  local:
    dataDir: /var/lib/etcd
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: v1.17.2
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
scheduler: {}

node-cidr-mask-size: "24" 의 내용을 "25"로 바꾸었습니다.

3-3. 변경 내용 검증

# kubeadm-config.yaml의 변경점을 확인합니다. 
kubeadm upgrade diff --config kubeadm-config.yaml

linux에서 diff를 한것과 같이, 어떤 내용이 변경이 되는지에 대해서 출력이 됩니다.

3-4. 업그레이드 시작

# upgrade 명령 수행
kubeadm upgrade apply --config kubeadm-config.yaml --ignore-preflight-errors all --force --v=5

적용 전에 --dry-run 옵션을 주게 되면, 테스트를 통해서 적용상 문제가 발생하는지를 볼 수 있습니다. 적용이 정상적으로 되면, kubeadm-config가 업데이트 됩니다.

3-5. 모든 마스터 노드에서 수행

위의 내용이 모두 수행되고나면, master 서버의 controller-manager, api-server, scheduler등이 새로이 생성됩니다. 이러한 수행을 다른 마스터 노드에서도 수행을 해주어야 정상적으로 반영이 됩니다.

4. 업그레이드 실패의 기록

와! 드디어 모든 kubeadm-config업데이트와 마스터 노드의 controller-manager가 신규 생성이 되었습니다. 프로세스를 확인해도 node-cider-mask가 원하는 숫자로 변경이 되어있습니다. 그러면 원하는대로 각 worker노드들의 아이피가 변경 됐는지를 체크해보겠습니다.

# 각 워커 노드의 cidr 점검
$ kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'
10.233.67.0/24 10.233.69.0/24 10.233.68.0/24 10.233.70.0/24 10.233.71.0/24 10.233.76.0/24 ...

어라? 분명히 25 로 변경이 됐는데, 왜 아직 반영이 안됐을까요? 이미 24기반으로 각 node들에서는 ip할당이 이루어졌기 때문에, pod, service등을 삭제 하지 않는 이상 적용한 cidr가 반영이 되지 않습니다. 결국, 업그레이드(cidr 변경)은 실패를 했고, 클러스터를 재설치 하기로 했습니다.

5. 쿠버네티스 재설치

필자는 kubespray를 통해서 클러스터를 구축했습니다. 그러면, 초기 구축할 당시 해당 인자를 설정해서 원하는 cidr로 조정을 하고 싶었습니다.
kubespray official github inventory

위의 inventory파일을 보게 되면, 기본적으로 클러스터 설치에 대한 설정들이 있습니다. 지금까지 기본 셋팅대로만 설치를 했었는데, node-cidr 조정(24 -> 25)를 하여 설치하기로했습니다. 노드에 대해서 ip할당을 줄여야 더 많은 노드들을 클러스터에 조인할 수 있기 때문입니다.

# Kubernetes internal network for services, unused block of space.
kube_service_addresses: 10.233.0.0/18

# internal network. When used, it will assign IP
# addresses from this range to individual pods.
# This network must be unused in your network infrastructure!
kube_pods_subnet: 10.233.64.0/18

# internal network node size allocation (optional). This is the size allocated
# to each node for pod IP address allocation. Note that the number of pods per node is
# also limited by the kubelet_max_pods variable which defaults to 110.
#
# Example:
# Up to 64 nodes and up to 254 or kubelet_max_pods (the lowest of the two) pods per node:
#  - kube_pods_subnet: 10.233.64.0/18
#  - kube_network_node_prefix: 24
#  - kubelet_max_pods: 110
#
# Example:
# Up to 128 nodes and up to 126 or kubelet_max_pods (the lowest of the two) pods per node:
#  - kube_pods_subnet: 10.233.64.0/18
#  - kube_network_node_prefix: 25
#  - kubelet_max_pods: 110
kube_network_node_prefix: 24

설정에도 설명이 잘되어있습니다. Up to 128 nodes 하기 위해서는 node의 cidr를 25로 사용하라고 나와있습니다. 수정하여 클러스터를 재설치를 완료했고, 특이사항이 없었습니다.

6. 마치며…

이번시간에는 클러스터의 CIDR 구성이 어떤식으로 되어있는지, 조정을 어떤식으로 진행해야되는지에 대해서 알아 보았습니다. 확실히 블로그를 찾아봐도 CIDR 조정에 관련해서는 절대 변경하지말것을 권고 하고 있습니다. 이는 이미 할당해버린 서비스들과 파드들이 삭제 되지 않는이상 재구성하기가 어렵기 때문입니다.

그 어떤 트러블 슈팅보다도 간단하지만 어려웠던 경험이였던 것 같습니다. 몇날 몇일을 야근하면서 설정을 변경하고 반영해보고 했는데, 해결은 아주 작은 부분이였습니다. 이처럼 초기 구축할 당시 설정들에 대해서 꼼꼼히 살펴보는 습관이 중요하다는것을 알게 됐습니다.

7. 참조

https://d-kuro.github.io/post/kubernetes-cidr/

kubeadm – How to “upgrade” (update) your configuration

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다