
온프래미스 쿠버네티스를 직접 구축하면서
쿠버네티스의 이해도를 높일 수 있고 다양한 트러블 슈팅을 경험할 수 있습니다.
따라서 이번 기회를 통해서
사용하던 K3s 클러스터(DePIN용)을 모두 지우고 새롭게 구축하려 합니다.
시리즈물의 각 글에서는 스스로 도전 과제를 정의하고 해결하는 과정을 기록합니다.
A. 실습 환경
모든 서버는 이 실습 환경 구성을 준수하였습니다.
서버 별로 추가적인 조치 사항은 필요한 시점에 작업하겠습니다.
이를 통해 각 작업의 의미를 이해하고 진행하면 좋을 것 같습니다.
A.1. 최소 및 권장 성능
A.2. Ubuntu OS 설치
A.1. 최소 및 권장 성능
원활한 실습을 위해서 충분한 리소스가 필요합니다.
이번 문서에서는 최소 CPU 2, RAM 4 정도면 진행할 수 있습니다.
하지만 원활히 실습을 원활하게 진행하기 위해서 CPU 4, RAM 16이 권장됩니다.
또한 GPU 워크로드 실습을 위해서는 최소한의 GPU가 필요할 수도 있습니다.
구분 | 제품 명 및 성능 | 수량 |
---|---|---|
CPU | Intel Core i5 8265U (3.90GHz) | 1 |
GPU | Intel UHD Graphics 620 | 1 |
RAM | 8 GBytes | 1 |
A.2. Ubuntu OS 설치
작년 4월에 만든 Boostable USB[A.2.1]을 사용해서 전체 포맷을 했습니다.
이후 Wi-FI 망을 통해서 SSH[A.2.2]로 접속하기 위한 라이브러리를 설치합니다.
sudo apt update -y
sudo apt install ssh
sudo apt install net-tools
설치가 되어 있다면 SSH 포트를 개방하고 시스템을 재부팅합니다.
(안전을 위해서 22가 아닌 다른 포트를 사용하는 것이 좋습니다.)
sed -i.bak 's/^#Port 22/Port 22/' /etc/ssh/sshd_config
sudo /etc/init.d/ssh restart
sudo netstat -anp|grep LISTEN|grep sshd
이후 Wi-Fi와 연결된 Wireless LAN Interface의 IP를 확인하고자 합니다.
같은 Wi-Fi에 연결된 장치들은 이 IP를 통해 통신을 주고받을 수 있습니다.
Wi-Fi 기계는 내부에 Private Network를 가지고 있습니다.
이에 따라 할당 가능한 IP를 IP Pool에 저장 및 기록하고 있습니다.
새로운 디바이스가 연결되면 신규 IP를 할당하고 이를 기록합니다.
아래 구문을 입력하면 Network Interface들을 볼 수 있습니다.
그 중에서 wlo(shortcut of Wireless LAN Interface)를 찾아서 확인합시다.
ifconfig
아래와 같은 내용이 나오는데 inet(IPv4), inet6(IPv6)를 찾아서 기억합시다.
wlo1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 000.000.000.000 netmask 255.255.255.0 broadcast 000.000.000.000
...
이제 같은 Wi-Fi에 연결한 다른 호스트에서 ssh로 접속이 가능해졌습니다.
ssh <username>@<inet-ip>
SSH 연결 이후,
Ubuntu 노트북을 덮으면 절전 모드(suspend)로 인해 프로세스가 멈춥니다.
따라서 logind[A.2.3]를 설정하고 시스템 재부팅을 진행합니다.
Ubuntu 22.04 LTS에서 HandleLidSwitch는 기본값이 Suspend입니다.
이는 절전모드를 뜻하기 때문에 모니터를 덮으면 프로세스도 멈춥니다.
따라서 절전모드를 비활성화하기 위해서 Ignore를 사용해야 합니다.
sudo cat /etc/systemd/logind.conf | grep "HandleLidSwitch="
#HandleLidSwitch=suspend
아래 구문을 실행하면 절전 모드가 되지 않습니다.
대신 항상 서버가 켜져있도록 전력을 공급해주어야 합니다.
또한 전기를 지속적으로 소모하기 때문에 사용하지 않으면 종료하도록 합시다.
sudo sed -i.bak 's/^#HandleLidSwitch=suspend/HandleLidSwitch=ignore/' /etc/systemd/logind.conf
sudo systemctl restart systemd-logind.service
A.3. vi 설정하기
일반적인 경우에는 nano를 사용해도 되지만,
kubectl edit 등을 사용하기 위해서 vi 설정도 진행하는것이 좋습니다.
sudo apt-get update
sudo apt-get install vim
B. 컨트롤 플레인(이하 cp-k8s)
공식 문서를 따라 하다 보면 뜻밖의 버그들을 마주합니다.
이 과정에서 다양한 버그를 겪으면서 해결방법을 고민하면 좋을 것 같습니다.
B.1. 구성 이해
쿠버네티스는 컨트롤 플레인과 워커 노드로 구분됩니다.
두 그룹을 별도로 배포하는 것이 권장되나 이번에는 한 서버만을 사용합니다.
왜 VMWare를 사용하지 않는가?
VMWare를 사용하면 1개 노트북에서 N개의 Guest OS를 설치할 수 있습니다.
하지만 2가지 이유로 VMWare를 사용하지 않고 Host OS를 그대로 사용했습니다.
1. VMWare의 Guest OS에 리소스를 할당할 만큼 노트북이 좋지 않습니다.
2. 공유 및 경합 등으로 cgroup, cpuset 테스트에 적합하지 않다 판단했습니다.
B.2. 사전 지식
쿠버네티스는 컨트롤 플레인과 워커노드로 구분됩니다.
그리고 각 구분 안에는 다양한 컴포넌트 들이 통신하게 됩니다.
이런 컴포넌트들은 실행 방식에 따라서 2가지로 분류할 수 있습니다.
Linux Service : Linux OS을 통해 실행될 서비스
Container : Container Runtime을 통해 실행될 서비스
따라서 실행된 방식에 따라서 시작, 확인, 종료, 로그 보는 방법이 다릅니다.
Linux Service
systemctl status kubelet.service
journalctl -u kubelet.service -f
Container
crictl pods 또는 crictl ps
crictl logs <ps_id>
이러한 구분은 컨트롤 플레인 트러블 슈팅에서 반드시 필요합니다.
예를 들어 kube-apiserver가 죽으면 kubectl을 사용할 수 없습니다.
그리고 etcd가 죽으면 kube-apiserver가 죽고 kubectl을 사용할 수 없습니다.
따라서 이런 경우에는 crictl을 사용한 트러블 슈팅이 필요해집니다.
추가적으로…
kubectl get pods -A는 전체 노드들의 파드를 출력하지만
crictl pods는 해당 노드의 파드만 출력하는 점을 주의해야 합니다.
- A host crictl pods → A host pods(container)
- B host circtl pods → B host pods(container)
- B host kubectl pods → All host pods
공식문서에서 사용하는 구성 요소와 실행 방식은 다음과 같습니다.
이름 | 커맨드 | 특징 |
---|---|---|
kubeadm | systemctl | kubernetes 구축을 위한 도구입니다. |
kubectl | systemctl | kube-apiserver와 통신하기 위한 도구입니다, |
kubelet | systemctl | kube-let으로 |
kube-apiserver | crictl | /etc/kubernetes/manifests 안에 정의 |
kube-controller-manager | crictl | /etc/kubernetes/manifests 안에 정의 |
kube-scheduler | crictl | /etc/kubernetes/manifests 안에 정의 |
etcd | crictl | /etc/kubernetes/manifests 안에 정의 |
B.3. containerd 런타임 설치하기
쿠버네티스는 다양한 컨테이너 런타임과 호환되기 위해서
Container Runtime Interface(CRI)라는 기술을 사용합니다.
기본값인 containerd[B.3.1]을 Docker Engine 방식[B.3.2]으로 설치했습니다.
왜 Binary 설치 방법을 사용하지 않나요?
지속적인 시스템 업그레이드 간의 편의성을 위해 Docker Engine을 선택했습니다.
두 방법의 비교는 별도의 글에서 다루고자 합니다.
혹시나 설치되있을 구 버전의 Docker Engine은 삭제하도록 합시다.
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
이후 아래의 7단계를 따라서 containerd를 설치하도록 합시다.
apt-get 패키지 색인 업데이트
sudo apt-get update
필수적인 라이브러리 설치
sudo apt-get install ca-certificates curl
ca-certificates : 인증서 관리
curl : HTTP/S 요청 전송 및 파일 다운로드용
Docker 공개 서명키 다운로드 및 준비
sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc
apt-get 패키지 저장소 추가
echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get 패키지 색인 업데이트
sudo apt-get update
패키지 다운로드
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
테스트
sudo docker run hello-world
B.4. containerd의 cgroup drvier 셋팅하기
Linux 계열의 OS는 리소스 할당 관리를 위해 제어 그룹(cgroup)을 사용합니다.
(자세한 내용은 cgroup, cpu 리뷰[B.4.1]에서 다루고 있습니다.)
쿠버네티스 또한 리소스 할당 관리를 위해 cgroup driver을 사용합니다.
설치한 containerd, kubelet는 각각의 cgroup driver를 사용하게 됩니다.
따라서 두 도구는 같은 종류의 cgroup driver를 사용할 것이 권장됩니다.
cgroup driver가 다를때 발생가능한 문제가 뭘까요?
공식 문서, 제 문서를 그대로 따라하면 이로 인한 문제를 겪게 됩니다.
그 문제가 무엇이 있고 어떻게 해결할 수 있는지 고민하면 좋을 것 같습니다.
지원되는 driver는 크게 2가지가 있으며 저는 기본값인 cgroupfs를 선택했습니다.
cgroupfs driver (default)
systemd driver
이후 아래의 8단계를 따라서 containerd의 cgroup driver를 설정합시다.
containerd 실행 확인
systemctl status containerd.service
containerd 설정파일 확인
cat /etc/containerd/config.toml cat /etc/containerd/config.toml | grep -v "#"
disabled_plugins = ["cri"]
crictl 상태 확인
contaienerd에서 비활성화된 상태이며, crictl info는 작동하지 않습니다.
crictl info
containerd 실행로그 확인
로그를 확인하면 Tracing, NRI intrace 등의 옵션을 제외하고
containerd 소켓은 정상적으로 작동하고 있는 것을 알고 있습니다.
(생각해보면 docker run hello-world가 성공하니 runtime에는 문제가 없다)Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095479877+09:00" level=info msg="skip loading plugin \"io.containerd.tracing.processor.v1.otlp\"..." error="skip plugin: tracing endpoint not configured" type=io.containerd.tracing.processor.v1 Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095492926+09:00" level=info msg="loading plugin \"io.containerd.internal.v1.tracing\"..." type=io.containerd.internal.v1 Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095507800+09:00" level=info msg="skip loading plugin \"io.containerd.internal.v1.tracing\"..." error="skip plugin: tracing endpoint not configured" type=io.containerd.internal.v1 Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095521275+09:00" level=info msg="loading plugin \"io.containerd.grpc.v1.healthcheck\"..." type=io.containerd.grpc.v1 Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095537389+09:00" level=info msg="loading plugin \"io.containerd.nri.v1.nri\"..." type=io.containerd.nri.v1 Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095551699+09:00" level=info msg="NRI interface is disabled by configuration." Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095746037+09:00" level=info msg=serving... address=/run/containerd/containerd.sock.ttrpc Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095794689+09:00" level=info msg=serving... address=/run/containerd/containerd.sock Feb 27 14:20:36 cp-k8s containerd[142973]: time="2025-02-27T14:20:36.095849575+09:00" level=info msg="containerd successfully booted in 0.020684s" Feb 27 14:20:36 cp-k8s systemd[1]: Started containerd container runtime.
contaienrd 설정하기
설정파일을 기본값[B.4.3]으로 설정하게 되면 이 문제가 해결됩니다.
백업하기
cp /etc/containerd/config.toml /etc/containerd/config.toml.bak
설정하기
containerd config default > /etc/containerd/config.toml
containerd 재시작하기
systemctl restart containerd
containerd 실행 확인하기
systemctl status containerd
crictl 실행 확인하기
crictl info crictl info | grep Endpoint
you should set the endpoint instead. "containerdEndpoint": "/run/containerd/containerd.sock",
B.5. IPv4 패킷 포워딩 승인
쿠버네티스 기본 네트워크는 IP 패킷 포워딩을 필요로 합니다.
하지만 Ubuntu Kernel의 IP 패킷 포워딩은 기본적으로 비활성화 입니다.
자세한 내용은 IPv4 packet forwarding 작동 과정[B.5.1]을 참고해주세요.
아래 구문을 통해서 패킷을 활성화 해야 합니다.
설정값 확인하기
sysctl net.ipv4.ip_forward
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.ipv4.ip_forward = 1 EOF
기본값 확인하기
sysctl net.ipv4.ip_forward
설정값 반영하기
sudo sysctl --system
변경값 확인하기
sysctl net.ipv4.ip_forward
B.6. Swap 설정하기
쿠버네티스의 노드에서 스왑 메모리는 성능 저하로 연결될 수 있다고 합니다.
이 내용의 AS-IS, TO-BE 테스트는 목적성의 범위를 벗어나 포함하지 않았습니다.
다만 공식문서 상의 권장사항이라 오차를 줄이기 위해 설정을 진행했습니다.
스왑 메모리 사용 여부 확인
사용하지 않거나, 사용한 적이 없다면 아무 출력이 나오지 않습니다.
swapon --show
제 환경에서는 스왑 메모리는 켜져있으나 사용한 적 없는 상태 같았습니다.
NAME TYPE SIZE USED PRIO /swapfile file 2G 0B -2
이 조치는 재부팅 이후로 초기화 되는 것으로 알고 있습니다.
따라서 임시 조치 이후 영구 조치를 같이 진행하고자 합니다.sudo swapoff -a
영구적으로 스왑 메모리 비활성화
백업
sudo cp /etc/fstab /etc/fstab.bak
수정
sudo vi /etc/fstab
/swapfile로 시작하는 구문을 찾아 주석처리 합시다.
# AS-IS /swapfile none swap sw 0 0 # TO-BE #/swapfile none swap sw 0 0
잔여 파일 정리
sudo rm /swapfile sudo find /swapfile
재부팅하기
sudo reboot
스왑 메모리 사용 여부 확인
swapon --show
1~4를 진행하고 재부팅하면 아무 메세지도 출력되지 않습니다.
B.7. kubeadm. kubelet, kubectl 설치하기
Installing kubeadm (Kubernetes v1.31)[B.7.1]을 따라합니다.
(25.02.27 기준, 한글 버전은 최신화 되지 않았으니 영어 버전을 사용해야 합니다.)
apt-get 패키지 색인 업데이트
sudo apt-get update
필수적인 라이브러리 설치
sudo apt-get install -y apt-transport-https ca-certificates curl
apt-transport-https : HTTPS를 통해 패키지를 다운로드 받을 수 있음
ca-certificates : 인증서 관리
curl
Google Cloud 공개 서명키 다운로드 및 준비
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
apt-get 패키지 저장소 추가
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
apt-get 패키지 색인 업데이트
sudo apt-get update
kubelet, kubeadm, kubectl 설치하기
sudo apt-get install -y kubelet kubeadm kubectl
만약 특정 버전만 설치하고 싶다면 아래 절차를 따라주세요.
sudo apt list -a kubelet sudo apt list -a kubectl sudo apt list -a kubectl sudo apt-get update sudo apt-get install -y kubelet="1.31.6-1.1" kubeadm="1.31.6-1.1" kubectl="1.31.6-1.1" sudo apt-mark hold kubelet kubeadm kubectl
kubelet, kubeadm, kubectl 버전 홀드하기
sudo apt-mark hold kubelet kubeadm kubectl
kubelet.service 실행하기
sudo systemctl enable --now kubelet
추후 kubeadm init을 통해서 kubelet을 비롯한 각종 설정을 진행합니다.
따라서 kubeadm init을 하기 전에 kubelet을 먼저 실행해두는 것이 좋습니다.kubelet.service 상태확인
sudo systemctl status kubelet.service
현재 kublet은 [에러 2]를 겪고 있으나, kubeadm init 이후 해결됩니다.
B.8. 컨트롤 플레인 준비
Creating a cluster with kubeadm[B.8.1]을 따라합니다.
(B.5과 동일한 맥락으로 가급적이면 영문 버전을 사용하는 것이 좋습니다.)
kubeadm 초기화하기
kubeadm init --pod-network-cidr=10.244.0.0/16
네트워크나 쿠버네티스의 각 CNI에 따라서 지정할 cidr가 달라질 수 있습니다.
제가 사용할 Flannel에서는 10.244.0.0/16을 사용하였습니다.출력된 설치 구문 백업하고 진행하기
kubectl config 파일 설정하기
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
CNI 설치하기 : 이 절차는 마지막에 진행합니다.
kubectl config 기본 경로 등록하기
export KUBECONFIG=/etc/kubernetes/admin.conf
kubeadm join : 이 절차는 진행하지 않고 백업만 해둡시다.
이 구분은 새 노드를 클러스터에 합류시키는 과정입니다.
저희는 단일 노드 클러스터를 배포 중이기에 필요없습니다.
여기까지 저는 쿠버네티스 공식 문서(v1.31)을 따라서 진행하였고
여러분도 100% 따라하셨다면 아래와 같은 에러를 마주하게 됩니다.
kubectl get pods -A
더 정확하게 말씀드리면 kube-apiserver가 주기적으로 종료되고 있습니다.
watch -n 0.5 "kubectl get pods -A"
E0228 20:16:15.389005 1818 memcache.go:265] couldn't get current server API group list: Get "https://127.0.0.1:6443/api?timeout=32s": dial tcp 127.0.0.1:6443: connect: connection refused
The connection to the server 127.0.0.1:6443 was refused - did you specify the right host or port?
kube-apiserver가 없으면 kubectl을 사용할 수 없고
이런 경우 B.1. 구성 이해[B.8.2]에서 말한것처럼 crictl을 사용해야 합니다.
다행히도 저희는 노드가 1개 이기 때문에 문제의 복잡성은 더 낮습니다.
전체 프로세스 확인하기
crictl ps -a watch -n 0.5 "crictl ps -a"
Every 0.5s: crictl ps -a cp-k8s: Fri Feb 28 23:25:27 2025 time="2025-02-28T23:25:27+09:00" level=warning msg="runtime connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead." time="2025-02-28T23:25:27+09:00" level=warning msg="image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As th e default settings are now deprecated, you should set the endpoint instead." CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD 9a087a7514a2f d2448f015605e About an hour ago Running kube-proxy 0 39151a8e805cf kube-proxy-lpjh5 a648e1b4e4cec 9195ad415d31e About an hour ago Running kube-scheduler 218 7fecd5f554731 kube-scheduler-cp-k8s cce7df40b2510 5f23cb154eea1 About an hour ago Running kube-controller-manager 0 bdf5b736db514 kube-controller-manager-cp-k8s 872513509c79f 1372127edc9da About an hour ago Running kube-apiserver 204 22f484462d2e1 kube-apiserver-cp-k8s 3966bdf7b3ce5 2e96e5913fc06 About an hour ago Running etcd 212 dca403bee9b52 etcd-cp-k8s
실행 중인 프로세스 확인하기
crictl ps -a watch -n 0.5 "crictl ps"
Every 0.5s: crictl ps -a cp-k8s: Fri Feb 28 23:25:27 2025 time="2025-02-28T23:25:27+09:00" level=warning msg="runtime connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead." time="2025-02-28T23:25:27+09:00" level=warning msg="image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As th e default settings are now deprecated, you should set the endpoint instead." CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD 9a087a7514a2f d2448f015605e About an hour ago Exited kube-proxy 0 39151a8e805cf kube-proxy-lpjh5 a648e1b4e4cec 9195ad415d31e About an hour ago Exited kube-scheduler 218 7fecd5f554731 kube-scheduler-cp-k8s cce7df40b2510 5f23cb154eea1 About an hour ago Exited kube-controller-manager 0 bdf5b736db514 kube-controller-manager-cp-k8s 872513509c79f 1372127edc9da About an hour ago Running kube-apiserver 204 22f484462d2e1 kube-apiserver-cp-k8s 3966bdf7b3ce5 2e96e5913fc06 About an hour ago Running etcd 212 dca403bee9b52 etcd-cp-k8s
kube-apiserver 로그 확인하기
crictl logs 872513509c79f -f
로그를 확인하면 알 수 있듯, etcd에 연결이 실패하면서 api-server가 다운됩니다.
W0227 07:37:04.476213 1 logging.go:55] [core] [Channel #2 SubChannel #4]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"\
etcd 로그 확인하기
crictl logs 3966bdf7b3ce5 -f
etcd는 다양한 이유로 종료되었지만 Terminated signal로 추정됩니다.
{"level":"info","ts":"2025-02-27T12:02:09.003552Z","caller":"osutil/interrupt_unix.go:64","msg":"received signal; shutting down","signal":"terminated"}
kubelet이 static pod(etcd, kube-apiserver, +)을 실행하는 만큼
kubelet이 문제의 원인이 될 수도 있을 것 같다는 생각이 듭니다.kubelet 로그 확인하기
journalctl -u kubelet.service | grep etcd
2379-2380(etcd) 등과 관련된 에러 로그들이 보입니다.
하지만 etcd가 Running일떄 아래를 실행해보면 모두 정상임을 알 수 있습니다.curl -k http://127.0.0.1:2380/ curl -k http://127.0.0.1:2381/readyz
혹은 6443(kube-apiserver)와 관련된 연결 에러도 볼 수 있습니다.
하지만 kube-apiserver가 Running일때 아래를 테스트하면 모두 성공합니다.nc 127.0.0.1 6443 -v Connection to 127.0.0.1 6443 port [tcp/*] succeeded! nc <wlo1 inet) 6443 -v Connection to <wlo1 inet> 6443 port [tcp/*] succeeded!
루프백 인터페이스(127.0.0.1)은 이상이 없습니다.
사설네트워크망(<wlo1 inet>)도 이상이 없으니 연결성 문제는 아닙니다.
만약 kube-apiserver가 계속 죽어있다면 아래 절차를 진행해보세요.
잠깐이지만 테스트를 진행할 수 있습니다.kubeadm reset kubeadm init nc 127.0.0.1 6443 -v Connection to 127.0.0.1 6443 port [tcp/*] succeeded! nc <wlo1 inet) 6443 -v Connection to <wlo1 inet> 6443 port [tcp/*] succeeded!
이 외에도 kube-controller-manager, kube-scheduler 로그를 확인해봐도
전체적으로 6443(kube-apiserver), 2379-2380(etcd) 연결 실패가 많습니다.
따라서 문제의 원인을 파악할 추가 정보가 없는 상태입니다.containerd 설정 변경
별도의 추가 연결고리가 없는 상태에서 Terminated Signal이 있는 것으로 보아
컨테이너 런타임에 관계된 kubelet, containerd 중 하나의 문제로 보입니다.
따라서 CrashLoopBackOff in kubelet, containerd 키워드로 검색합니다.CrashLoopBackOff in kubelet, containerd 현재 사용 중인 containerd 버전은 1.7.25을 쓰고 있기에,
CrashLoopBackOff - … containerd 1.5.5 [B.8.3]을 참고했습니다.containerd -v containerd containerd.io 1.7.25 bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
containerd의 SystemdCgroup 확인
cat /etc/containerd/config.toml | grep SystemdCgroup
SystemdCgroup = false
containerd의 SystemCgroup 변경 (+백업)
sed -i.bak 's/^ SystemdCgroup = false/ SystemdCgroup = true/' /etc/containerd/config.toml
containerd의 SystemdCgroup 확인
cat /etc/containerd/config.toml | grep SystemdCgroup
SystemdCgroup = true
containerd, kubelet 재시작
systemctl restart containerd systemctl restart kubelet
CoreDNS Pending 이슈 발생
이후 일부 파드에서 CoreDNS가 Pending 상태인 것을 확인하였습니다.
그 이유는 현재 등록된 유일한 Node가 NotReady 상태여서 그렇습니다.kubectl get pods -A kubectl describe pod coredns-7c65d6cfc9-8xspj -n kube-system
노드를 확인해보면 유일한 노드인 cp-k8s가 NotReady임을 알 수 있습니다.
그리고 그 이유는 설치된 cni plugin이 없어서 발생합니다.kubectl get nodes -A kubectl describe node cp-k8s
container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:cni plugin not initialized
CNI 용 FlannelCNI 설치하기
따라서 CNI 목록[B.6.4]에서 Flannel CNI[B.6.5]를 설치해서 다운로드 합니다.
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
하지만 이후 Flannel CNI가 지속적으로 다운되는 문제가 발생합니다.
로그를 확인하면 네트워크 브릿지 설정 쪽에서 문제가 발생한 것을 알 수 있습니다.kubectl get pods -A kubectl logs kube-flannel-ds-skbps -n kube-flannel
W0228 12:53:42.056638 1 client_config.go:618] Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work. I0228 12:53:42.064103 1 kube.go:139] Waiting 10m0s for node controller to sync I0228 12:53:42.064145 1 kube.go:469] Starting kube subnet manager I0228 12:53:43.064380 1 kube.go:146] Node controller sync successful I0228 12:53:43.064440 1 main.go:231] Created subnet manager: Kubernetes Subnet Manager - cp-k8s I0228 12:53:43.064453 1 main.go:234] Installing signal handlers I0228 12:53:43.064749 1 main.go:468] Found network config - Backend type: vxlan E0228 12:53:43.064864 1 main.go:268] Failed to check br_netfilter: stat /proc/sys/net/bridge/bridge-nf-call-iptables: no such file or directory
B.9. containerd 런타임 변경하기
kubectl get pods -A
kubectl describe pod coredns-7c65d6cfc9-8xspj -n kube-system
노드를 확인해보면 유일한 노드인 cp-k8s가 NotReady임을 알 수 있습니다.
그리고 그 이유는 설치된 cni plugin이 없어서 발생합니다.
kubectl get nodes -A
kubectl describe node cp-k8s
container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:cni plugin not initialized
이는 B-5. IPv4 패킷 포워딩 승인[B-8.3]을 통해서 해결할 수 있습니다.
다만 기존에 사용하지 않는 기능이 있기 떄문에, 몇 가지 모듈 추가가 필요합니다.
sudo mkdir -p /etc/modules-load.d/
sudo tee /etc/modules-load.d/containerd.conf > /dev/null << EOF
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl net.ipv4.ip_forward
sysctl net.bridge.bridge-nf-call-iptables
sysctl net.bridge.bridge-nf-call-ip6tables
sudo sysctl --system
sysctl net.ipv4.ip_forward
sysctl net.bridge.bridge-nf-call-iptables
sysctl net.bridge.bridge-nf-call-ip6tables
이후 containrd를 재시작하고 경우에 따라서 에러가 발생했던 파드를 제거해주세요.
systemctl restart containerd.service
B.10. taints 제거하기 [단일 노드인 경우]
현재 노드가 1개 뿐이며 control-plane으로 쓰기 위해 taint되어 있습니다.
따라서 원활한 실습 진행을 위해 임시로 untaint하도록 하겠습니다.
샘플 파드 배포하기
kubectl run nginx --image=nginx
파드 배포 상태 확인하기
kubectl get pod nginx kubectl describe pod nginx
Node(cp-k8s) taints 확인하기
kubectl get nodes cp-k8s -o yaml | grep taints -A 2
taints: - effect: NoSchedule key: node-role.kubernetes.io/control-plane
Node(cp-k8s) taints 제거하기
kubectl taint node cp-k8s node-role.kubernetes.io/control-plane:NoSchedule-
파드 배포 상태 확인하기
kubectl get pod nginx kubectl describe pod nginx
테스트 파드 제거하기
kubectl delete pod nginx
[에러]
본 문서에서 겪은 여러 에러들에 대한 이야기를 다룹니다.
[에러 1] The repository ‘~’ does not have a Release file.
잘못된 apt-get 저장소를 입력하면 나타내는 에러입니다.
현재 kubeadm 설치하기 [에러 1.1]는 최신화 되어 있지 않아 에러를 낼 수 있습니다.
따라서 Installing kubeadm [에러 1.2]를 사용하면 이 문제를 겪지 않습니다.
에러 메세지는 다음과 같습니다.
sudo apt-get update
Hit:2 http://security.ubuntu.com/ubuntu jammy-security InRelease
Hit:3 http://kr.archive.ubuntu.com/ubuntu jammy InRelease
Hit:4 http://kr.archive.ubuntu.com/ubuntu jammy-updates InRelease
Ign:1 https://packages.cloud.google.com/apt kubernetes-xenial InRelease
Err:5 https://packages.cloud.google.com/apt kubernetes-xenial Release
404 Not Found [IP: 142.251.42.174 443]
Hit:6 http://kr.archive.ubuntu.com/ubuntu jammy-backports InRelease
Reading package lists... Done
E: The repository 'https://apt.kubernetes.io kubernetes-xenial Release' does not have a Release file.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details.
root@cp-k8s:~# cat /etc/apt/sources.list.d/kubernetes.list
deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main
[에러 2] failed to load Kubelet config file
kubelet.service가 config file을 찾지 못해서 발생한 에러입니다.
이 문서에서는 kubeadm init 전에 kubelet를 확인하면 이 에러를 볼 수 있습니다.
따라서 절차대로 진행한 후에 kubeadm init 후의 로그를 확인하면 해결이 됩니다.
root@cp-k8s:~# find /var/lib/kubelet/config.yaml
find: ‘/var/lib/kubelet/config.yaml’: No such file or directory
[에러 3] CrashLoopBackOff - After update to containerd
B.4 [에러 3.1]에서 고른 cgropufs을 B.9 [에러 3.2]에서 systemd로 변경했습니다.
따라서 이번에는 kubelet의 cgroup driver를 변경하는 방법을 안내합니다.
kubelet.service 설정파일 위치 확인하기
systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled) Drop-In: /usr/lib/systemd/system/kubelet.service.d └─10-kubeadm.conf
kubelet.service 설정구성 파악하기
cd /usr/lib/systemd/system/kubelet.service.d ls 10-kubeadm.conf cat 10-kubeadm.conf # Note: This dropin only works with kubeadm and kubelet v1.11+ [Service] Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" # This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env # This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use # the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file. EnvironmentFile=-/etc/default/kubelet ExecStart= ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
kubelet.service의 cgroup driver 확인하기
cat /var/lib/kubelet/config.yaml | grep cgroupDriver
cgroupDriver: systemd