개요
단일 노드화된 경량 쿠버네티스(k3s)로 구현되어 있는 TrueNAS의 Apps기능을 이용하다가, 순정 쿠버네티스(?)에 관심이 생겨 시작했다가, 수많은 오류를 해결하기 위해 긴 시간 구글링하며 배운 것들을 기록하기 위해 글을 작성합니다.
3대의 마스터 노드, 3대의 워커 노드로 구성하고 Ansible을 작동시킬 별도의 노드 1대를 추가로 구성했고, TrueNAS에서 총 7대의 VM을 사용했습니다.
사용한 OS는 Rocky Linux 9.4이며 Ansible 버전은 2.25, 파이썬 버전은 3.12입니다.
- Ansible : 10.50.10.21
- Master1 : 10.50.10.31 (k8smaster1)
- Master2 : 10.50.10.32 (k8smaster2)
- Master3 : 10.50.10.33 (k8smaster3)
- Worker1 : 10.50.10.23 (k8sslave1)
- Worker2 : 10.50.10.24 (k8sslave2)
- Worker3 : 10.50.10.25 (k8sslave3)
Master, Worker 구성
아래 모든 과정은 root로 진행합니다. 마스터와 워커 노드 모두 적용되어야 합니다.
Static IP & SSH
- Static IP
Ansible은 별도의 agent를 사용하지 않고 SSH를 사용합니다. 그렇기 때문에, 마스터 노드와 워커 노드 모두 SSH를 사용할 수 있어야 하고, 고정된 IP를 가질 필요가 있습니다.
GUI를 사용할 경우 설정 – 네트워크에서 아래와 같이 설정할 수 있고,
터미널을 이용할 경우 nmtui를 사용해 구성하실 수 있습니다.
제 경우 VM간 통신을 위한 가상 브릿지를 사용했기 때문에, 게이트웨이를 구성하지 않았지만, 실제 인터넷 연결에 사용되는 NIC라면 게이트웨이까지 입력해 주시면 됩니다.
- SSH
Rocky Linux는 SSH패키지가 기본으로 설치 및 활성화되어 있기 때문에, PermitRootLogin옵션만 yes로 변경했습니다.
yes로 변경한 이유는 아래에서 설명드리겠습니다. 모든 과정이 끝난 후엔 해당 옵션을 다시 prohibit-password로 변경할 예정입니다.
변경 후 아래와 같이 입력해 SSH를 재시작해야 합니다.
service sshd restart
스왑 메모리 비활성화
쿠버네티스는 기본적으로 스왑 메모리를 지원하지 않고, 활성화되어있을 경우 정상적으로 실행되지 않습니다. 강제로 사용한다 하여도 노드 간 성능편차를 예측할 수 없어 기본적으로 비활성화하는 과정을 거치게 됩니다.
아래 명령어를 입력해 비활성화 시켜 줍니다.
swapoff -ased -i 's/.*swap.*/#&/' /etc/fstab
/etc/fstab의 내용 중 정규표현식으로 .*swap.* 패턴, 즉, 무작위 문자열 사이 swap이 들어간 행 전체를 주석처리(#&)하라는 명령어입니다.
아래처럼 nano /etc/fstab 해서 swap항목을 직접 주석처리해도 됩니다.
방화벽 설정
마스터 노드와 워커 노드에서 열어야 하는 포트가 각각 다릅니다.
아래 명령어를 입력해 포트를 열고 방화벽을 재시작합니다.
- 마스터 노드
firewall-cmd --add-port=6443/tcp --permanent
firewall-cmd --add-port=2379-2380/tcp --permanent
firewall-cmd --add-port=4789/udp --permanent
firewall-cmd --add-port=10250/tcp --permanent
firewall-cmd --add-port=10251/tcp --permanent
firewall-cmd --add-port=10252/tcp --permanent
firewall-cmd --add-port=10255/tcp --permanent
firewall-cmd --reload
- 워커 노드
firewall-cmd --add-port=4789/udp --permanent
firewall-cmd --add-port=6783/tcp --permanent
firewall-cmd --add-port=10250/tcp --permanent
firewall-cmd --add-port=10252/tcp --permanent
firewall-cmd --add-port=30000-32767/tcp --permanent
firewall-cmd --reload
제어 노드 구성
Ansible은 노드를 크게 2가지로 나누어 제어 노드(Control Node)와 매니지드 노드(Managed Node)로 구분하고 제어 노드가 매니지드 노드를 관리, 컨트롤하도록 합니다.
보통은 마스터 노드에서 ansible까지 같이 사용하는 편인 것 같은데, 가시성과 관리를 위해 ansible 전용 VM을 하나 생성하고 여기서 다루겠습니다.
이 VM이 제어 노드, 나머지 쿠버네티스 마스터 노드 3개와 워커 노드 3개가 ansible에선 매니지드 노드가 되는 겁니다.
키 쌍 생성 & 공개키 복사
먼저 아래 명령어를 통해 SSH Key 쌍을 하나 생성합니다. 키 암호화(passphrase)는 패스(공란)합니다.
ssh-keygen -t ed25519 -b 4096
그러면 아래와 같이 한 쌍의 키(id_ed25519, id_ed25519.pub)가 생성된 것을 확인할 수 있습니다.
키 생성을 확인한 후 임의로 키 이름을 변경했습니다(id_ed25519 → k8s_fenta).
이후 ssh-copy-id를 사용해 마스터 노드 3대와 워커 노드 3대에 공개키를 복사하겠습니다.
이 과정에서 각 노드의 root패스워드로 로그인을 한 번씩 해야 하기 때문에, PermitRootLogin을 잠시 yes로 변경한 것이죠.
~/.ssh 경로에서 진행하시면 됩니다.
[root@k8s-bootstrap .ssh]# ssh-copy-id -i k8s_fenta.pub root@10.50.10.23
그러면 아래와 같은 메세지가 출력되는 것을 확인할 수 있습니다.
/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "k8s_fenta.pub"
The authenticity of host '10.50.10.23 (10.50.10.23)' can't be established.
ED25519 key fingerprint is
This host key is known by the following other names/addresses:
~/.ssh/known_hosts:1: 10.50.10.22
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@10.50.10.23's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'root@10.50.10.23'"
and check to make sure that only the key(s) you wanted were added.
키 추가가 완료되었다면, 해당 노드의 sshd_config를 수정하여 PermitRootLogin값을 prohibit-password로 변경하고, 재시작 해 줍니다.
다시 제어 노드로 돌아와서 키를 이용해 로그인이 잘 되는 것을 확인합니다.
모든 노드에 해당 과정을 반복합니다.
ssh-copy-id -i {private key} root@10.50.10.24
ssh-copy-id -i {private key} root@10.50.10.25
ssh-copy-id -i {private key} root@10.50.10.31
ssh-copy-id -i {private key} root@10.50.10.32
ssh-copy-id -i {private key} root@10.50.10.33
Python, Git 설치
Kubespray는 파이썬으로 작성되어 있기 때문에, 동작을 위해선 파이썬이 필요합니다.
Rocky Linux는 기본으로 파이썬을 포함하고 있긴 하지만 버전 9.4 기준, 파이썬의 버전이 3.9.18이기 때문에, 최신 Kubespray와는 호환이 되지 않습니다.
그러므로 아래처럼 Git과 함께 3.12를 설치해 줍니다.
dnf install -y python3.12 git python3-pip
Kubespray 설치
Github의 Kubespray 버전을 확인합니다. 24년 6월 5일 기준 v2.25.0이네요.
아래와 같이 입력해 로컬에 코드를 받아옵니다. 루트에서 실행할 경우 ~/kubespray의 경로에 저장됩니다.
git clone -b v2.25.0 https://github.com/kubernetes-sigs/kubespray.git
파이썬 가상환경(Python venv)에 Kubespray 환경 구성
파이썬으로 작성된 코드를 작동하기 위해서는 코드에 사용된 패키지들이 필요합니다.
보통 이 패키지들은 pip를 이용해 작성된 requirements.txt에 정의되고, 사용자는 해당 패키지를 읽어 그대로 설치할 수 있도록 되어 있습니다.
그렇지만, 시간이 지남에 따라 파이썬과 여러 패키지들이 업데이트 되며, 패키지간의 의존성이나, 코드 동작 방식 등이 변경되며 특정 패키지가 사용 불가능해지고, 프로그램 전체가 작동 불능이 되는 경우도 생깁니다. 혹은, 다른 프로그램을 사용하기 위해 설치해야 하는 의존패키지가 Ansible에서 요구하는 패키지의 버전과 다르거나 하는 등의 문제로 충돌을 일으킬 수도 있죠.
그래서, Ansible 공식 가이드에서는 기본적으로 파이썬 가상환경을 이용해 작동하는 것을 권장합니다.
이 글에서는 공식 가이드에서 권장하는 대로 가상환경을 이용해 Ansible을 사용하겠습니다.
여러 줄의 명령어를 입력해야 하므로 스크립트로 만들겠습니다. ~/ (루트 경로, 혹은 kubespray폴더가 있는 곳.)에서 만들어주세요.
nano kubespray-venv.sh
아래 내용을 복사 붙여넣기 합니다.
파이썬 3.12버전으로 가상 환경을 만들고 해당 가상환경 안에 kubespray에 필요한 의존 패키지를 설치하는 명령입니다.
#/bin/bash
VENVDIR=kubespray-venv
KUBESPRAYDIR=kubespray
python3.12 -m venv $VENVDIR
source $VENVDIR/bin/activate
python3.12 -m pip install --upgrade pip
cd $KUBESPRAYDIR
pip install -U -r requirements.txt
작성이 끝났다면 실행 권한을 부여합니다.
chmod +x kubespray-venv.sh
이제 해당 스크립트를 실행하면 자동으로 파이썬 가상환경을 구성하고 kubespray의 requirements를 읽어 설치해 줍니다.
./kubespray-venv.sh
아래와 같이 출력되면 정상적으로 수행된 것입니다.
(Kubespray는 파이썬 버전에 따른 의존 패키지들과 ansible까지 모두 모아 도커 이미지로도 배포하고 있습니다. 파이썬 가상환경 사용에 익숙치 않은 분들은 해당 도커 이미지를 이용할 수도 있습니다.)
쿠버네티스 설치
인벤토리(inventory.ini) 작성
제어 노드의 kubespray폴더 안에는 inventory폴더와 그 하위에 k8s배포를 위한 sample폴더가 있습니다. 그리고 그 안에 k8s구성을 입력할 inventory.ini가 있습니다.
(~/kubespray/inventory/sample/inventory.ini)
이 샘플 폴더를 복사한 뒤 그 안의 inventory.ini를 편집하겠습니다.
cd kubespraycp -rpf inventory/sample inventory/k8s_fentanano inventory/k8s_fenta/inventory.ini
그럼 아래와 같이 예시로 작성된 inventory.ini를 확인할 수 있습니다.
제가 구성하려는 환경에 맞게 아래와 같이 입력했습니다.
ansible_host값은 외부 접속등으로 달라질 수 있는 접속 경로를 위해 사용하는 옵션입니다. 기본적으로 실제로 접속 가능한 주소를 사용해야 하기 때문에, 이 글에서는 ip주소를 그대로 사용했습니다.
etcd는 키:값 쌍으로 이루어진 데이터베이스로 쿠버네티스 클러스터의 모든 정보를 담고 있습니다. 이것만 잘 보존하고 있다면 클러스터가 유실되어도 복구할 수 있으나, 잃어버리면 복구할 수 없다고 합니다.
etcd는 보통 1개의 단일 노드로 이루어지거나 3개 이상의 홀수로 구성됩니다. 마스터 노드와 별개로 구성하는 경우도 있으나, 대체로 마스터 노드가 담당한다고 합니다.
따라서, k8smaster1(그리고 2, 3)에 etcd_member_name=etcd1(및 2, 3)을 지정했습니다.
ansible_ssh_private_key_file은 ssh로 접속할 때 사용할 키를 지정하는 옵션으로 이 글에서는 ~/.ssh/k8s_fenta, 즉, /root/.ssh/k8s_fenta가 됩니다.
마스터, 워커 노드 모두 동일한 키를 사용하도록 구성했으므로 똑같은 옵션을 붙여주면 됩니다.
hosts.yaml 작성
아래 명령어를 입력합니다.
괄호 안의 IP는 개인 환경에 맞는 IP를 넣어주셔야 합니다.
declare -a IPS=(10.50.10.31 10.50.10.32 10.50.10.33 10.50.10.23 10.50.10.24 10.50.10.25)CONFIG_FILE=inventory/k8s_fenta/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}
아래와 같이 출력됨을 확인할 수 있습니다.
그런데 control_plane이 총 3개인데, 2개만 추가된 것을 확인할 수 있습니다. 내용을 확인해 보겠습니다.
nano inventory/k8s_fenta/hosts.yaml
node3이 빠져있으니, 추가하고 저장하겠습니다.
그리고 각 노드에 키 파일을 지정해 줍니다.
혹은 이 글처럼 모두 동일한 키를 사용한다면 hosts.yml아래에 아래와 같이 입력할 수도 있습니다.
vars:
ansible_ssh_private_key_file: /root/.ssh/k8s_fenta
이후, hosts.yml를 이용해서 쉘 명령어를 한 번에 실행시킬 수도 있습니다.
ansible all -i inventory/k8s_fenta/hosts.yaml -m shell -b --become-user root -a "command"
특정 노드에만 실행시키려면 아래와 같이 사용하면 됩니다.
ansible all -i inventory/k8s_fenta/hosts.yaml -m shell -b --become-user root -a "command" -l node1,node2,node3
애드온(addons.yml) 작성
위에서 복사한 폴더 속, group_vars폴더 하위에 addons.yml이라는 파일이 있습니다.
해당 파일을 열어보면,
nano inventory/k8s_fenta/group_vars/k8s_cluster/addons.yml
아래처럼 쿠버네시트 배포 시 필요한 기능을 함께 배포할 수 있도록 되어 있습니다.
저는 헬름과 대시보드를 true로 구성했습니다.
# Kubernetes dashboard
# RBAC required. see docs/getting-started.md for access details.
dashboard_enabled: true
# Helm deployment
helm_enabled: true
이 외에도 metric server, ingress-nginx, metallb 등의 애드온을 설정 및 자동배포할 수 있는데, 다른 글을 통해서 따로따로 설치해보겠습니다.
kube_proxy_strict_arp: true 적용
온프레미스 환경에서 사용 가능한 로드밸런서(MetalLB)를 사용하기 위해선 kube_proxy_strict_arp환경 값을 true로 바꿔주어야 합니다.
쿠버네티스가 모두 배포된 후에도 수정할 수 있지만, 배포 전 미리 Ansible에서 해당 값을 수정해 주겠습니다.
nano inventory/k8s_fenta/group_vars/k8s_cluster/k8s_cluster.yml
내리다보면 해당값을 찾을 수 있습니다.
Kubespray로 K8S 배포
위에서 Kubespray를 실행할 환경으로 가상환경을 구성했습니다.
가상환경으로 전환하는 방법은 쉘에서 source {가상환경이름}/bin/activate 명령을 사용하면 됩니다.
(alias를 이용하면 추후 사용 시 편하게 사용할 수 있습니다. 저는 bashrc에 alias kv=’source kubespray-venv/bin/activate’ 해놓고 kv를 입력해 활성화시킵니다.)
스크립트를 생성할 때 가상환경 이름을 kubespray-venv로 지정했기 때문에, 최종적으로 아래와 같이 입력하게 됩니다.
[root@k8s-bootstrap ~]# source kubespray-venv/bin/activate
입력하고 나면 root앞에 (kubespray-venv)라는 이름이 붙는 것을 확인할 수 있습니다.
이제 kubespray-venv 가상환경(파이썬 3.12 및 requirements.txt의 패키지가 모두 설치된)에서 kubespray폴더로 이동한 뒤, ansible을 이용해 각 노드에 핑테스트를 진행합니다.
(kubespray-venv) [root@k8s-bootstrap ~]# cd kubespray
(kubespray-venv) [root@k8s-bootstrap kubespray]# ansible all -m ping -i inventory/k8s_fenta/inventory.ini
아래와 같이 출력되면 정상적으로 통신할 수 있습니다.
조금 전 작성한 inventory.ini를 이용해 쿠버네티스 배포를 시작하면 됩니다.
-vvv옵션은 상세한 로그를 출력하는 옵션이고 -b –become-user root는 root계정을 사용하겠다는 뜻입니다.
ansible-playbook -i inventory/k8s_fenta/inventory.ini cluster.yml -e ansible_ssh_timeout=50 -vvv -b --become-user root
최종적으로 아래와 같이 출력(failed=0)되면 성공적으로 배포 완료된 것입니다.
맨 처음 kubectl을 사용하기 위해선 쉘에서 alias kubectl=’/usr/local/bin/kubectl’ 명령을 사용해야 합니다.
이후 해당 명령을 계속해서 사용하기 위해선 .bashrc에 해당 내용을 추가하면 됩니다.
nano ~/.bashrc
Ansible 제어 노드에서 아래 명령어를 사용해 한 방에 kubectl, kubeadm, kubelet을 추가하고 활성화할 수도 있습니다.
ansible all -i inventory/k8s_fenta/hosts.yaml -m shell -b --become-user root -a 'echo -e "\nalias kubectl='\''/usr/local/bin/kubectl'\''\nalias kubelet='\''/usr/local/bin/kubelet'\''\nalias kubeadm='\''/usr/local/bin/kubeadm'\''" >> ~/.bashrc && source ~/.bashrc' -l node1,node2,node3
이후 아래처럼 각 노드에서 정상적으로 작동중인 쿠버네티스를 확인할 수 있습니다.
마스터 노드에서 워커 노드에 라벨을 추가해 줍니다.
kubectl label nodes k8sslave1 node-role.kubernetes.io/worker=worker
kubectl label nodes k8sslave2 node-role.kubernetes.io/worker=worker
kubectl label nodes k8sslave3 node-role.kubernetes.io/worker=worker
node/k8sslave1 labeled
node/k8sslave2 labeled
node/k8sslave3 labeled
Calico vxlan 방화벽 정책 생성하기
쿠버네티스 배포 완료는 확인하였지만, 아래 명령어를 사용해 모든 네임스페이스(Namespace)의 파드를 살펴보면 몇몇 파드가 CrashLoopBackOff 오류를 뿜고 있는 것을 확인할 수 있습니다.
kubectl get pods -A -o wide
로그를 살펴 보겠습니다.
kubectl logs -n kube-system calico-kube-controllers-68485cbf9c-mzk8m
같은 내용(2줄)이 반복되는 것을 확인할 수 있습니다.
Error getting cluster information config ClusterInformation="default" error=Get "https://10.233.0.1:443/apis/crd.projectcalico.org/v1/clusterinformations/default": dial tcp 10.233.0.1:443: connect: no route to host
Failed to initialize datastore error=Get "https://10.233.0.1:443/apis/crd.projectcalico.org/v1/clusterinformations/default": dial tcp 10.233.0.1:443: connect: no route to host
Calico가 쿠버네티스 API에 접근할 수 없다는 뜻입니다. 기본적으로 Kubespray를 이용해 설치 시 Calico는 기본적으로 vxlan모드를 사용하게 되고, 각 노드에 이를 위한 인터페이스(vxlan.calico, cali)를 생성합니다.
그러나, 방화벽 기본 정책으로 인해 해당 인터페이스로 들어오는 통신이 모두 차단되어 이러한 문제가 발생하게 됩니다.
아래와 같이 vxlan.calico와 cali인터페이스로 들어오는 연결을 수락(ACCEPT)함으로써 문제를 해결할 수 있습니다.
name=calicoAccept
sudo firewall-cmd --permanent --new-zone={name}
sudo firewall-cmd --permanent --zone={name} --set-target=ACCEPT
sudo firewall-cmd --permanent --zone={name} --add-interface=vxlan.calico
sudo firewall-cmd --permanent --zone={name} --add-interface="cali+"
sudo firewall-cmd --reload
노드마다 실행해 줄 필요가 있으니, Ansible제어 노드에서 한 번에 실행해 보겠습니다.
ansible all -i inventory/k8s_fenta/hosts.yaml -m shell -a 'firewall-cmd --permanent --new-zone=calicoAccept && firewall-cmd --permanent --zone=calicoAccept --set-target=ACCEPT && firewall-cmd --permanent --zone=calicoAccept --add-interface=vxlan.calico && firewall-cmd --permanent --zone=calicoAccept --add-interface="cali+" && firewall-cmd --reload' -b --become-user root
[WARNING]: Skipping callback plugin 'ara_default', unable to load
node1 | CHANGED | rc=0 >>
success
success
node2 | CHANGED | rc=0 >>
success
success
node5 | CHANGED | rc=0 >>
success
success
node3 | CHANGED | rc=0 >>
success
success
node4 | CHANGED | rc=0 >>
success
success
node6 | CHANGED | rc=0 >>
success
success
이후, 잠시 기다리면 아래처럼 정상화 된 파드를 확인할 수 있습니다.
kubectl logs -n kube-system calico-kube-controllers-68485cbf9c-rl5vf
2024-06-12 06:28:39.200 [INFO][1] main.go 107: Loaded configuration from environment config=&config.Config{LogLevel:"info", WorkloadEndpointWorkers:1, ProfileWorkers:1, PolicyWorkers:1, NodeWorkers:1, Kubeconfig:"", DatastoreType:"kubernetes"}
024-06-12 06:28:39.224 [WARNING][1] winutils.go 144: Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.
2024-06-12 06:28:39.225 [INFO][1] main.go 131: Ensuring Calico datastore is initialized
2024-06-12 06:28:39.247 [INFO][1] main.go 157: Calico datastore is initialized
2024-06-12 06:28:39.248 [INFO][1] main.go 194: Getting initial config snapshot from datastore
2024-06-12 06:28:39.273 [INFO][1] main.go 197: Got initial config snapshot
2024-06-12 06:28:39.282 [INFO][1] watchersyncer.go 89: Start called
2024-06-12 06:28:39.282 [INFO][1] main.go 211: Starting status report routine
2024-06-12 06:28:39.282 [INFO][1] main.go 220: Starting Prometheus metrics server on port 9094
2024-06-12 06:28:39.282 [INFO][1] main.go 503: Starting informer informer=&cache.sharedIndexInformer{indexer:(*cache.cache)(0xc000275ae8), controller:cache.Controller(nil), processor:(*cache.sharedProcessor)(0xc0003c45f0), cacheMutationDetector:cache.dummyMutationDetector{}, listerWatcher:(*cache.ListWatch)(0xc000275ad0), objectType:(*v1.Pod)(0xc0006a3200), objectDescription:"", resyncCheckPeriod:0, defaultEventHandlerResyncPeriod:0, clock:(*clock.RealClock)(0x31e05c0), started:false, stopped:false, startedLock:sync.Mutex{state:0, sema:0x0}, blockDeltas:sync.Mutex{state:0, sema:0x0}, watchErrorHandler:(cache.WatchErrorHandler)(nil), transform:(cache.TransformFunc)(nil)}
2024-06-12 06:28:39.282 [INFO][1] main.go 503: Starting informer informer=&cache.sharedIndexInformer{indexer:(*cache.cache)(0xc000275b30), controller:cache.Controller(nil), processor:(*cache.sharedProcessor)(0xc0003c4640), cacheMutationDetector:cache.dummyMutationDetector{}, listerWatcher:(*cache.ListWatch)(0xc000275b18), objectType:(*v1.Node)(0xc000591600), objectDescription:"", resyncCheckPeriod:0, defaultEventHandlerResyncPeriod:0, clock:(*clock.RealClock)(0x31e05c0), started:false, stopped:false, startedLock:sync.Mutex{state:0, sema:0x0}, blockDeltas:sync.Mutex{state:0, sema:0x0}, watchErrorHandler:(cache.WatchErrorHandler)(nil), transform:(cache.TransformFunc)(nil)}
2024-06-12 06:28:39.282 [INFO][1] main.go 509: Starting controller ControllerType="Node"
2024-06-12 06:28:39.282 [INFO][1] controller.go 158: Starting Node controller
2024-06-12 06:28:39.284 [INFO][1] watchersyncer.go 130: Sending status update Status=wait-for-ready
2024-06-12 06:28:39.284 [INFO][1] syncer.go 86: Node controller syncer status updated: wait-for-ready
2024-06-12 06:28:39.284 [INFO][1] watchersyncer.go 149: Starting main event processing loop
2024-06-12 06:28:39.285 [INFO][1] watchercache.go 181: Full resync is required ListRoot="/calico/resources/v3/projectcalico.org/nodes"
2024-06-12 06:28:39.285 [INFO][1] watchercache.go 181: Full resync is required ListRoot="/calico/ipam/v2/assignment/"
2024-06-12 06:28:39.285 [INFO][1] watchercache.go 181: Full resync is required ListRoot="/calico/resources/v3/projectcalico.org/ippools"
2024-06-12 06:28:39.285 [INFO][1] watchercache.go 181: Full resync is required ListRoot="/calico/resources/v3/projectcalico.org/clusterinformations"
I0612 06:28:39.286624 1 shared_informer.go:311] Waiting for caches to sync for nodes
2024-06-12 06:28:39.288 [INFO][1] resources.go 350: Main client watcher loop
2024-06-12 06:28:39.289 [INFO][1] watchercache.go 294: Sending synced update ListRoot="/calico/resources/v3/projectcalico.org/clusterinformations"
2024-06-12 06:28:39.289 [INFO][1] watchersyncer.go 130: Sending status update Status=resync
2024-06-12 06:28:39.289 [INFO][1] syncer.go 86: Node controller syncer status updated: resync
2024-06-12 06:28:39.289 [INFO][1] watchersyncer.go 209: Received InSync event from one of the watcher caches
2024-06-12 06:28:39.291 [INFO][1] watchercache.go 294: Sending synced update ListRoot="/calico/resources/v3/projectcalico.org/ippools"
2024-06-12 06:28:39.291 [INFO][1] watchersyncer.go 209: Received InSync event from one of the watcher caches
2024-06-12 06:28:39.293 [INFO][1] watchercache.go 294: Sending synced update ListRoot="/calico/ipam/v2/assignment/"
2024-06-12 06:28:39.293 [INFO][1] watchersyncer.go 209: Received InSync event from one of the watcher caches
2024-06-12 06:28:39.294 [INFO][1] watchercache.go 294: Sending synced update ListRoot="/calico/resources/v3/projectcalico.org/nodes"
2024-06-12 06:28:39.294 [INFO][1] watchersyncer.go 209: Received InSync event from one of the watcher caches
2024-06-12 06:28:39.294 [INFO][1] watchersyncer.go 221: All watchers have sync'd data - sending data and final sync
2024-06-12 06:28:39.295 [INFO][1] watchersyncer.go 130: Sending status update Status=in-sync
2024-06-12 06:28:39.295 [INFO][1] syncer.go 86: Node controller syncer status updated: in-sync
2024-06-12 06:28:39.308 [INFO][1] hostendpoints.go 173: successfully synced all hostendpoints
I0612 06:28:39.387735 1 shared_informer.go:318] Caches are synced for nodes
I0612 06:28:39.387766 1 shared_informer.go:311] Waiting for caches to sync for pods
I0612 06:28:39.387771 1 shared_informer.go:318] Caches are synced for pods
2024-06-12 06:28:39.387 [INFO][1] ipam.go 251: Will run periodic IPAM sync every 7m30s
2024-06-12 06:28:39.388 [INFO][1] ipam.go 315: Syncer is InSync, kicking sync channel status=in-sync
문제 해결(Trouble-Shooting)
‘Could not set transient hostname: Invalid hostname’
맨 처음 inventory.ini에 마스터 및 워커 노드의 이름을 정할 때 k8s_master, k8s_slave와 같은 형태로 언더바를 넣었었는데, 해당 특수문자가 오류를 일으켰습니다.
hostname에 지정한 특수문자 등을 제거한 후 재시도 해서 해결했습니다.
‘groups.etcd | length is not divisibleby 2’
이 글을 처음 작성할 땐 etcd가 ‘홀수’로 구성되어야 한다는 지식이 없어서 2개의 노드로 구성했었습니다.
검색 후 노드를 하나 늘려 3개로 맞춰주자 해결되었습니다.
‘failed to fetch endpoints from etcd cluster member list’
“failed to fetch endpoints from etcd cluster member list: context deadline exceeded”가 출력되었던 경우입니다.
etcd설치 중 문제가 생긴 노드가 발생하거나 이미 설치된 기존 etcd에 문제가 있을 때, 해당 노드의 etcd를 정리하지 않고 ansible만 재실행 할 경우 발생하는 에러라고 합니다.
sudo rm -rf /var/lib/etcd/* sudo rm -f /etc/systemd/system/etcd*
이후 다시 제어 노드에서 ansible을 이용해 구성하면 됩니다.
‘unable to start service etcd’
“unable to start service etcd: Job for etcd.service failed because a timeout was exceeded.”
해당 오류가 발생한 노드에서 journalctl -xeu etcd.service를 통해 에러 로그를 확인했더니,
아래처럼 오류가 출력되고 있었습니다.
“msg”:”failed to find remote peer in cluster”,”local-member-id”:”9aa1ed3b1653e57b”,”remote-peer-id-stream-handler”
이 오류는 제가 inventory.ini에서 etcd_member_name에 복사-붙여넣기 중 실수로 인해 두 개의 노드에 동일한 중복값을 넣어서 발생했습니다. IP가 다른 두 대의 노드에 같은 이름을 넣어 한 쪽에서 에러를 일으켰던 것이죠.
etcd를 초기화하고 inventory.ini를 제대로 수정한 뒤 오류가 해결되었습니다.
'no route to host(CoreDNS)'
해당 문제는 kubespray에서 CNI기본으로 사용하는 Calico설치가 제대로 이루어지지 않았을 경우에 발생합니다.
Calico를 제대로 구성한 이후에도 문제가 발생한다면, 일시적으로 firewall-cmd로 443포트를 열어서 해결할 수도 있습니다.
관련 글
2025.01.23 - [Kubernetes] - 홈서버에서 쿠버네티스 사용을 위한 기본 환경 구성하기
홈서버에서 쿠버네티스 사용을 위한 기본 환경 구성하기
개요쿠버네티스 설치를 완료했다면, 실사용하기 위해 필요한 것들을 설치할 차례입니다. NFS로 볼륨을 구성하기 위한 csi-driver-nfs, (정확하지 않은 표현이지만) 리버스 프록시 역할을 해줄 Ingress-N
worklazy.net
2025.01.23 - [Kubernetes] - Kubespray를 이용하여 K8S 클러스터 업그레이드 하기
Kubespray를 이용하여 K8S 클러스터 업그레이드 하기
개요최초 Kubespray글을 작성했을 때의 쿠버네티스 버전은 1.29.5였습니다.[root@k8smaster1 ~]# kubectl get nodesNAME STATUS ROLES AGE VERSIONk8smaster1 Ready control-plane 74d v1.29.5k8smaster2 Ready control-plane 74d v1.29.5k8smaster3
worklazy.net
출처
1. https://github.com/kubernetes-sigs/kubespray
GitHub - kubernetes-sigs/kubespray: Deploy a Production Ready Kubernetes Cluster
Deploy a Production Ready Kubernetes Cluster. Contribute to kubernetes-sigs/kubespray development by creating an account on GitHub.
github.com
2. https://github.com/kubernetes-sigs/kubespray/issues/8374
Error: failed to fetch endpoints from etcd cluster member list: context deadline exceeded · Issue #8374 · kubernetes-sigs/kube
Environment: OS (printf "$(uname -srm)\n$(cat /etc/os-release)\n"): Ubuntu 18.04.6 LTS Version of Ansible (ansible --version): ansible 2.9.6 Version of Python (python --version): Python 2.7.17 Kube...
github.com
3. https://quay.io/repository/kubespray/kubespray?tab=tags&
Quay
quay.io
4. https://shonm.tistory.com/751
kubespray 로 kubernetes 설치 (ubuntu)
0. 설치 환경 kubspray 서버 Ubuntu , CPU 2, Memory 2G Hostname : k8sbootstrap ip : 192.168.101.143 Control plane 1대 Ubuntu , CPU 2, Memory 2G Hostname : k8smaster ip : 192.168.101.144 Worker node 3대 Ubuntu cpu 2, memory 2G 1번 worker node Hostnam
shonm.tistory.com
5. https://github.com/kubernetes-sigs/kubespray/blob/master/docs/ansible/ansible.md
kubespray/docs/ansible/ansible.md at master · kubernetes-sigs/kubespray
Deploy a Production Ready Kubernetes Cluster. Contribute to kubernetes-sigs/kubespray development by creating an account on GitHub.
github.com
6. https://memory-hub.tistory.com/8
[k8s] Kubespray로 Kubernetes 설치하기
Kubespray는 쿠버네티스를 쉽게 설치하는 자동화 도구입니다. Ansible의 playbook과 inventory 설정을 통해서 Kubernetes 클러스터를 설정해 보고자 합니다. Kubernetes는 최소 1개의 Mater Node와 1개의 Worker Node 1
memory-hub.tistory.com
7. https://nirsa.tistory.com/120
[Kubernetes] kubespray를 이용해 쿠버네티스 설치하기 (GCP, CentOS7)
설치 환경 GCP (Google Cloud Platform) CentOS 7.7 1908 Master Node 3대, Worker Node 2대 (VM 인스턴스 총 5대) GCP VM 인스턴스 준비 연습용으로 하실 경우 VM 인스턴스를 생성할줄 모르신다면 https://nirsa.tistory.com/entry
nirsa.tistory.com
8. https://wikidocs.net/130113
2-개념
앤서블을 이용하는데 알아두어야 할 기본 개념에 대해서 알아보겠습니다.  not working
I've bringing up Kubernetes cluster with calico as CNI on CentOS 7 with firewall enabled. I've master and worker nodes. I was able to bring up cluster and able to list the nodes and Kubernetes system
stackoverflow.com