# 02 — k3s 두 노드 ## 이 단계가 끝나면 `kubectl get nodes` 에 두 노드가 `Ready` 로 나온다. ## 전제 [01](../01-vms/) 이 끝나 두 게스트에 SSH 가 붙는다. --- ## 1. server 를 깐다 (kc-lab-1) **하기** ```bash ssh kc-lab-1 curl -sfL https://get.k3s.io | sudo sh -s - server --node-ip 192.168.122.11 ``` `--node-ip` 를 준다. 게스트에 인터페이스가 여럿이면 k3s 가 엉뚱한 것을 고를 수 있고, 그러면 두 노드가 서로를 다른 주소로 알게 된다. **확인** ```bash sudo kubectl get nodes sudo systemctl is-active k3s ``` ## 2. 토큰을 꺼낸다 **하기** — 화면에 찍어 눈으로 옮기지 말고 변수로 받는다 ```bash TOKEN=$(ssh kc-lab-1 'sudo cat /var/lib/rancher/k3s/server/node-token') echo "${#TOKEN} 자" # 값이 아니라 길이만 확인한다 ``` **실측** — 이 실험대에서는 56자였다. 0 이면 server 가 아직 안 떴거나 경로가 다르다. ## 3. agent 를 붙인다 (kc-lab-2) **하기** — 토큰을 그대로 넘긴다 ```bash ssh kc-lab-2 "curl -sfL https://get.k3s.io | sudo sh -s - agent \ --server https://192.168.122.11:6443 \ --token '$TOKEN' \ --node-ip 192.168.122.12" ``` > 토큰을 셸 히스토리에 남기고 싶지 않으면 파일로 넘긴다. > ```bash > ssh kc-lab-1 'sudo cat /var/lib/rancher/k3s/server/node-token' \ > | ssh kc-lab-2 'sudo tee /tmp/token >/dev/null' > ssh kc-lab-2 "curl -sfL https://get.k3s.io | sudo sh -s - agent \ > --server https://192.168.122.11:6443 --token-file /tmp/token \ > --node-ip 192.168.122.12; rm -f /tmp/token" > ``` **확인** — server 쪽에서 ```bash sudo kubectl get nodes -o wide ``` **실측** ``` kc-lab-1 Ready control-plane v1.36.4+k3s1 192.168.122.11 kc-lab-2 Ready v1.36.4+k3s1 192.168.122.12 ``` `` 은 오류가 아니라 **역할 라벨이 없다**는 뜻이다. agent 는 원래 그렇다. ## 4. 유닛 이름이 다르다 | 노드 | 유닛 | |---|---| | server | `k3s.service` | | agent | `k3s-agent.service` | **확인** ```bash ssh kc-lab-1 'systemctl cat k3s | grep -A3 ExecStart=' ssh kc-lab-2 'systemctl cat k3s-agent | grep -A4 ExecStart=' ``` **실측** ``` ExecStart=/usr/local/bin/k3s server '--node-ip' '192.168.122.11' ExecStart=/usr/local/bin/k3s agent '--node-ip' '192.168.122.12' ``` > 이 차이가 A-4 에서 결과를 갈랐다. server 노드를 잃으면 `kubectl` 자체가 > 불통이 되고, agent 를 잃으면 `kubectl` 은 되지만 그 위의 워크로드가 사라진다. ## 5. k3s 가 기본으로 딸려 오는 것 따로 설치하지 않아도 이미 있다. | | 무엇 | |---|---| | Traefik | 인그레스 컨트롤러. `:80` 을 듣는다 | | servicelb (klipper-lb) | LoadBalancer 타입을 호스트 포트로 매핑 | | local-path | 기본 StorageClass. **노드 로컬 디스크** | | flannel | 파드 네트워크 (VXLAN) | | kube-router | NetworkPolicy 집행 | **확인** ```bash sudo kubectl get pods -A sudo kubectl get storageclass ``` > `local-path` 가 기본이라는 것이 A-4 에서 비용을 청구한다. PVC 가 **만들어진 > 노드에 묶여** 다른 노드로 재배치되지 않는다. ## 6. 워크스테이션에서 쓰려면 **하기** ```bash ssh kc-lab-1 'sudo cat /etc/rancher/k3s/k3s.yaml' > ~/.kube/kc-lab.yaml sed -i 's|127.0.0.1|192.168.122.11|' ~/.kube/kc-lab.yaml export KUBECONFIG=~/.kube/kc-lab.yaml ``` kubeconfig 안의 서버 주소가 `127.0.0.1` 이다. **게스트 안에서만 맞는 주소**라 밖에서 쓰려면 바꿔야 한다. --- ## 막히면 | 증상 | 원인 | 확인 | |---|---|---| | agent 가 `NotReady` | 토큰·주소 오타 | `journalctl -u k3s-agent -n 30` | | 노드 IP 가 예상과 다름 | `--node-ip` 없이 설치 | `kubectl get nodes -o wide` | | 밖에서 kubectl 이 안 붙음 | kubeconfig 의 `127.0.0.1` | 위 6번 | | 파드가 한 노드에만 몰림 | 스케줄러 판단 | `topologySpreadConstraints` 로 강제 |