LabHub
开始
学习 学习路径 课程

用 Kubespray 与 Terraform 搭建集群

Kubespray 会替你修复的,以及它并不知道的

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

한 줄 요약

kubespray 는 스왑·모듈·sysctl 을 스스로 맞춰 주지만, 남이 잡은 포트와 나중에 값을 되돌리는 설정 파일은 모릅니다. 설치 전 점검은 그 경계를 아는 일입니다.

왜 이게 필요했나

kubespray 설치는 한 번에 수 분이 걸립니다. 그 수 분 뒤에 실패하면 원인은 대개 설치 전에 이미 있던 것입니다. 그리고 그런 실패는 오류 문구가 원인을 가리키지 않습니다. 10250 을 다른 프로세스가 잡고 있으면 첫 kubeadm init 이 preflight 의 [ERROR Port-10250] 로 막힙니다. 그런데 kubespray 는 첫 시도가 kubelet 을 이미 띄웠을 수 있다고 보고, 재시도에서 Port-10250 을 무시 목록에 넣어 다시 돌립니다(roles/kubernetes/control-plane/tasks/kubeadm-setup.yml). 그러면 원인을 말하는 줄은 로그 위쪽 첫 시도 출력에만 남고, 눈에 띄는 마지막 오류는 전혀 다른 말을 합니다. 첫 재부팅 뒤 파드끼리 통신이 끊기는데 kubespray 로그는 전부 초록색입니다. 이런 일을 설치 뒤에 거꾸로 추적하는 것보다, 설치 전에 호스트가 무엇을 받아 줄 수 있는지 보는 편이 훨씬 쌉니다.

어떻게 동작하나

쿠버네티스가 노드에 요구하는 것은 공식 문서에 흩어져 있습니다. 모아 보면 네 갈래입니다.

스왑        kubelet 은 기본값(failSwapOn: true)으로 스왑이 켜져 있으면 시작을 거부한다
커널 모듈    overlay(containerd 의 기본 스냅샷터) · br_netfilter(브리지 트래픽이 iptables 를 거치게)
sysctl      net.ipv4.ip_forward = 1 · net.bridge.bridge-nf-call-iptables = 1
포트        6443(API) · 2379-2380(etcd) · 10250(kubelet) · 10257(controller-manager) · 10259(scheduler)

kubespray 는 이 가운데 앞의 셋을 역할로 처리합니다. roles/kubernetes/preinstall/tasks/0010-swapoff.yml 은 fstab 에서 스왑 줄을 지우고, swap.target 을 가려(mask) 다시 켜지지 않게 하고, swapoff -a 를 부릅니다. 이 작업은 kubelet_fail_swap_on 이 참일 때만 돌고, 기본값이 참입니다. 스왑을 쓰고 싶으면 이 값을 거짓으로 두고 kubelet 이 swapBehavior: LimitedSwap 로 뜨게 하는 길도 열려 있습니다. br_netfilter 는 node 역할이 올리고 /etc/modules-load.d/kubespray-br_netfilter.conf 로 남깁니다. ip_forward 같은 값은 sysctl_file_path(기본 /etc/sysctl.d/99-sysctl.conf)에 적습니다. 우분투에서는 이 파일이 ../sysctl.conf 를 가리키는 심링크라 kubespray 가 링크를 따라가 /etc/sysctl.conf 에 적는데(0080-system-configurations.yml), 부팅 때 읽히는 자리는 여전히 99-sysctl.conf 라는 이름의 순서입니다.

포트는 다릅니다. kubespray 에는 남의 프로세스를 멈추는 작업이 없고, 있어서도 안 됩니다. 무엇이 포트를 잡고 있는지는 그 서버의 사정이기 때문입니다. 그래서 포트는 사람이 먼저 봅니다. ss -ltnp 가 PID 를 보여 주고, 그 PID 를 띄운 systemd 유닛을 찾아 멈추고 disable 해야 합니다. 프로세스만 죽이면 Restart=always 유닛이 2초 만에 되살립니다.

sysctl 에는 순서의 함정이 있습니다. 부팅 때 systemd-sysctl 이, 그리고 사람이 sysctl --system 을 돌릴 때 procps 가 /etc/sysctl.d·/run/sysctl.d·/usr/lib/sysctl.d*.conf파일 이름순으로 읽고, 같은 키는 나중에 읽은 값이 이깁니다. 이름이 같으면 /etc 쪽이 이깁니다. 그래서 k8s.conf 에 ip_forward = 1 을 적어도 zz-hardening.conf 가 0 을 적어 두면, 지금은 1 인데 재부팅하면 0 이 됩니다. kubespray 가 쓰는 99-sysctl.conf 도 같은 규칙 안에 있습니다 — 숫자는 영문자보다 앞에 정렬되므로 이름이 영문자로 시작하는 파일은 모두 그 뒤에 읽힙니다.

마지막으로 kubespray 자신의 사전 검사가 있습니다. 0040-verify-settings.yml 은 Ansible 이 모은 사실(facts)로 판정합니다. 컨트롤 플레인 노드의 메모리가 minimal_master_memory_mb(1500MB)보다 작으면 멈추고, 지원하지 않는 배포판이면 멈춥니다. 그리고 ip 변수를 주지 않으면 facts 의 기본 경로 주소(default_ipv4)에 API 서버와 etcd 를 붙입니다. 인터페이스가 여럿인 서버에서 엉뚱한 망에 컨트롤 플레인이 붙는 사고가 여기서 나옵니다.

현장에서 만나는 모습

이 모듈의 실습 VM 에는 세 가지 흔적을 일부러 남겨 두었습니다. 모두 실제로 흔한 모양입니다. 첫째, 메모리가 모자라던 시절 누군가 스왑 파일을 만들고 fstab 에 적어 두었습니다. 둘째, 옛 모니터링 에이전트가 10250 에서 HTTP 를 받고 있습니다 — kubelet 과 같은 포트라 설치 뒤에 kubelet 이 bind: address already in use 로 재시작을 반복하게 됩니다. 셋째, 보안 점검이 "라우팅 금지" 로 ip_forward 를 0 으로 박은 sysctl 파일을 남겼는데, 이름이 zz- 로 시작해 어떤 쿠버네티스 설정보다 늦게 읽힙니다.

이 셋 가운데 kubespray 가 알아서 해결하는 것은 스왑 하나뿐입니다. 포트는 손대지 않고, sysctl 은 설치 순간에는 값을 켜 두지만 재부팅 때 zz- 파일이 다시 0 으로 되돌립니다. 그래서 현장의 점검표는 "무엇을 설정했나" 가 아니라 "재부팅 뒤에도 그런가" 를 묻습니다.

다음 실습에서 할 것

고치기 전의 상태를 먼저 기록하고, 스왑을 지금과 부팅 때 모두 끕니다. 모듈을 올리고 sysctl 을 적용한 뒤 zz- 파일 때문에 재부팅 값이 되돌아가는 것을 확인해 고칩니다. 10250 을 잡은 유닛을 찾아 멈추고, kubespray 가 보는 facts 를 모아 최소 메모리와 비교합니다. 마지막으로 역할 코드를 읽어 네 문제를 누가 고치는지 가릅니다.