API 서버로 가는 경로가 끊긴 클러스터 — Calico 순환 의존
쿠버네티스를 직접 구축해보면서 그 위에 이런저런 앱도 올려 테스트해볼까 해서 클러스터를 하나
만들었습니다. AWS EC2 두 대에 Kubespray 로 올렸고 노드 2개는 마스터 1대와 워커 1대(worker1)로
나눴습니다. Kubespray 는 Ansible 로 서버에 쿠버네티스를 깔아주는 도구입니다.
설치는 끝났는데 거의 모든 파드가 비정상이었습니다. 클러스터 안의 이름을 IP 로 풀어 주는
CoreDNS, 파드끼리 통신하게 해 주는 Calico 같은 시스템 파드부터 FluxCD 와 Ingress Nginx 까지
상태가 제각각이었습니다 — ContainerCreating 에서 안 넘어가거나, Pending 이거나,
Terminating 으로 멈춰 있거나, 아예 Unknown 이었습니다.
그중 시작점으로 보인 게 워커 노드 하나라 거기부터 확인했습니다.
kubelet의 잘못된 API 서버 주소 — localhost
worker1 이 NotReady 라, 설치가 덜 끝난 줄 알고 노드에 들어가 kubelet 로그부터 봤습니다.
$ sudo journalctl -u kubelet -f
Unable to register node with API server
Post "https://localhost:6443/api/v1/nodes":
dial tcp 127.0.0.1:6443: connect: connection refused
워커 노드에는 API 서버가 없으니 localhost:6443 에는 아무것도 안 떠 있어서 연결이 거부됩니다.
Kubespray 설치 과정에서 kubeconfig 의 server 주소가 잘못 잡힌 상태였습니다.
/etc/kubernetes/kubelet.conf 의 clusters 섹션에서 server 를 마스터 노드의 실제 내부 IP 로
바꿨습니다.
# 전
server: https://localhost:6443
# 후
server: https://<master-node-ip>:6443
sudo systemctl daemon-reload && sudo systemctl restart kubelet
노드는 Ready 로 넘어왔지만 파드는 여전히 안 떴습니다. 노드가 안 붙어서 생긴 일이라고 봤지만
그게 전부는 아니었던 겁니다.
Calico ↔ API 서버 순환 의존
calico-node 파드들이 Init:Error 와 Init:CrashLoopBackOff 를 오갔습니다.
$ kubectl logs calico-node-xxxxx -c install-cni
Unable to create token for CNI kubeconfig
error=Post "https://<service-cluster-ip>:443/...":
dial tcp <service-cluster-ip>:443: i/o timeout
여기에 순환 구조가 있습니다.
Calico (install-cni)
│ API 서버에 접근해야 시작됨
▼
클러스터 IP 경로 (= API 서버로 가는 길)
│ 이 경로가 뚫리려면 파드 네트워크가 있어야 함
▼
파드 네트워크
│ 그런데 이걸 만드는 게…
└────────▶ 다시 Calico (한 바퀴 도는 순환)
install-cni 가 API 서버에 접근할 때 쓰는 주소가 Kubernetes 서비스의 클러스터 IP 인데, 그
주소로 가는 경로가 아직 안 열려 있었습니다. 어디까지 확인했느냐면 i/o timeout 이 난다는
것까지고, 클러스터 IP 를 거치지 않게 바꾸니 바로 떴습니다.
클러스터 IP 를 거치지 않고 API 서버로 직접 가게 했고, 환경변수 두 개면 됩니다.
env:
- name: KUBERNETES_SERVICE_HOST
value: "<master-node-ip>"
- name: KUBERNETES_SERVICE_PORT
value: "6443"
install-cni init 컨테이너와 calico-node 컨테이너 양쪽에 넣어야 합니다.
kubectl apply — immutable field 충돌
DaemonSet 을 YAML 로 뽑아 고치고 다시 적용하려는데 거부당합니다.
kubectl get daemonset calico-node -n kube-system -o yaml > calico-node-fix.yaml
kubectl get -o yaml 은 서버가 관리하는 필드까지 전부 뱉으니, 이걸 그대로 apply 하면 바꿀 수
없는 값을 바꾸려 한다고 막힙니다.
지워야 하는 것들입니다.
metadata.creationTimestamp
metadata.generation
metadata.resourceVersion
metadata.uid
status: (섹션 전체)
지운 뒤 적용합니다.
kubectl apply -f calico-node-fix.yaml --force
이렇게 넣자 Calico 파드가 전부 1/1 Running 이 됐습니다.
CoreDNS — 루프와 API 접근 두 문제
Calico 를 고치자 이번엔 CoreDNS 가 다른 에러를 냈습니다.
[FATAL] plugin/loop: Loop detected
Corefile 의 forward . /etc/resolv.conf 가 원인입니다. 노드의 /etc/resolv.conf 가 클러스터
DNS 를 가리키고 있으면, CoreDNS 가 자기한테 쿼리를 되던지는 루프가 됩니다.
/etc/resolv.conf 를 따라가지 말고 외부 DNS 를 직접 적습니다.
# 전
forward . /etc/resolv.conf
# 후
forward . 8.8.8.8 8.8.4.4
그러자 아까 Calico 에서 본 것과 같은 에러가 나왔습니다.
failed to list *v1.Namespace: ... dial tcp <service-cluster-ip>:443: i/o timeout
CoreDNS 도 inClusterConfig 로 클러스터 IP 를 잡아 API 서버에 붙기 때문에 같은 환경변수를
Deployment 에 넣어 직접 연결로 바꿨습니다.
Ingress·FluxCD도 같은 원인
ingress-nginx 컨트롤러 로그입니다.
Creating API client" host="https://<service-cluster-ip>:443"
connect: connection refused
FluxCD 컨트롤러들도 CrashLoopBackOff 였고, 둘 다 API 서버에 붙어야 동작하는 컴포넌트라 붙는
방식도 앞의 것들과 같습니다. KUBERNETES_SERVICE_HOST 와 KUBERNETES_SERVICE_PORT 를 각
Deployment 에 넣자 그대로 올라왔습니다.
증상은 여럿, 원인은 API 서버로 가는 경로 하나
증상은 “앱이 전부 죽는다”였지만 원인은 하나였습니다. API 서버로 가는 경로.
파드가 API 서버에 붙는 기본 방식은 inClusterConfig 이고 이건 Kubernetes 서비스의 클러스터 IP
를 씁니다. 그 IP 로 가는 경로가 깨지면 API 서버에 의존하는 컴포넌트가 전부 같이 죽습니다. CNI,
DNS, Ingress 컨트롤러, GitOps 컨트롤러가 모두 여기 해당합니다.
증상이 컴포넌트마다 다르게 보여서 각각 다른 문제처럼 느껴지지만 에러 메시지에 같은 IP 와 같은 포트가 반복해서 나오면 그게 신호입니다.
댓글 0