LabHub
시작하기
배우기 러닝패스 코스

kubespray 와 Terraform 으로 클러스터 세우기

cluster.yml 을 두 번 돌려도 같은 클러스터다

LabHub 에서 이어서 보기

목표

준비된 인벤토리로 cluster.yml 을 돌려 VM 한 대(4 vCPU · 4 GiB)에 쿠버네티스 1.35.8 을 세웁니다. 같은 플레이북을 한 번 더 돌려 무엇이 다시 바뀌는지 보고, 값 하나를 바꿔 태그로 그 부분만 다시 적용합니다.

왜 중요한가

kubespray 의 운영 방식은 "선언을 고치고 같은 플레이북을 다시 돌린다" 입니다. 노드를 더할 때도, 설정을 바꿀 때도 같은 cluster.yml 을 돌립니다. 그래서 두 번째 실행이 무엇을 바꾸는지 알아야 합니다 — 멱등이라는 말은 changed 가 0 이라는 뜻이 아니고, 매번 바뀌는 작업이 무엇인지 알아야 "이번 실행이 의도한 것만 바꿨는가" 를 판단할 수 있습니다. 긴 플레이북의 로그를 끝에서부터 읽는 법(PLAY RECAP 과 TASKS RECAP)도 여기서 익힙니다. 이 실습은 설치 두 번과 부분 실행 한 번에 모두 약 15분을 기다립니다. 세션이 60분이니 여유가 모자라면 연장하세요. VM 이 끝나면 클러스터도 사라집니다.

단계

  1. /opt/ks/kubespray 에서 ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml --list-tasks 로 플레이 목록을 보고 /root/ks/install/plan.jsonplay_count(play 개수), etcd_play("Install etcd" play 의 번호), cni_play("Invoke kubeadm and install a CNI" 의 번호), apps_play("Install Kubernetes apps" 의 번호)를 숫자로 적으세요.
  2. /opt/ks/kubespray 에서 ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml 을 돌리고 출력 전체를 /root/ks/logs/cluster-1.log 에 남기세요(| tee 나 리다이렉트). 약 7분 걸립니다. 끝나면 PLAY RECAP 의 node1 이 failed=0 이고, kubectl get node node1 이 Ready 여야 합니다.
  3. /root/ks/install/run1.json 에 첫 실행의 ok, changed, failed(PLAY RECAP 의 node1 값), elapsed_sec(TASKS RECAP 머리에 찍힌 누적 시간을 초로, 정수), slowest_task(TASKS RECAP 목록 맨 위 작업 이름, 역할 접두사 포함 그대로), node_uid(kubectl get node node1 의 metadata.uid)를 적으세요.
  4. 같은 명령을 한 번 더 돌려 출력 전체를 /root/ks/logs/cluster-2.log 에 남기세요. 두 번째도 failed=0 이어야 하고, 노드 UID 가 3단계에서 적은 값과 같아야 합니다(클러스터를 새로 만들지 않았다는 뜻).
  5. 두 번째 실행에서 changed: [node1] 을 낸 작업의 이름을 로그의 TASK [...](핸들러는 RUNNING HANDLER [...]) 괄호 안 그대로(역할 접두사 포함) 한 줄에 하나씩 /root/ks/install/changed.txt 에 적으세요. 그리고 /root/ks/install/run2.json 에 두 번째 실행의 ok, changed, failed, elapsed_sec 을 적습니다.
  6. /root/ks/inventory/lab/group_vars/all/containerd.ymlcontainerd_max_container_log_line_size: 32768 을 더하고, cluster.yml --tags containerd 로 컨테이너 런타임 부분만 다시 돌려 출력 전체를 /root/ks/logs/containerd.log 에 남기세요. 끝나면 /etc/containerd/config.tomlmax_container_log_line_size 가 32768 이고 containerd 가 새 설정으로 다시 떠 있어야 하며, 노드는 여전히 Ready 여야 합니다.
  7. /root/ks/install/report.jsonkubespray(태그), server_version(API 서버의 gitVersion), first_run_sec, second_run_sec(두 실행의 누적 시간, 정수), first_changed, second_changed, tags_run_changed(6단계 실행의 changed), containerd_restarted(6단계로 containerd 가 다시 떴는가, 불리언)를 적으세요.

참고

돌리기 전에 순서를 본다

/opt/ks/kubespray 에서 ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml --list-tasks 로 플레이 목록을 보고 /root/ks/install/plan.jsonplay_count(play 개수), etcd_play("Install etcd" play 의 번호), cni_play("Invoke kubeadm and install a CNI" 의 번호), apps_play("Install Kubernetes apps" 의 번호)를 숫자로 적으세요.

--list-tasks 는 아무것도 바꾸지 않고 플레이북을 펼쳐 보여 줍니다. 출력의 play #N (호스트 패턴): 이름 줄을 보세요. etcd 가 컨트롤 플레인보다, CNI 가 애드온보다 먼저 오는 이유를 생각해 보면 뒤에서 실패 지점을 읽기가 쉬워집니다.

cluster.yml 을 돌린다

/opt/ks/kubespray 에서 ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml 을 돌리고 출력 전체를 /root/ks/logs/cluster-1.log 에 남기세요(| tee 나 리다이렉트). 약 7분 걸립니다. 끝나면 PLAY RECAP 의 node1 이 failed=0 이고, kubectl get node node1 이 Ready 여야 합니다.

콘솔이 끊기면 플레이북도 같이 죽을 수 있습니다. systemd-run --unit=<이름> --setenv=HOME=/root ... 이나 tmux 로 띄우면 터미널과 무관하게 돌고, tail -f 로 지켜볼 수 있습니다. HOME 이 비어 있으면 kubespray 의 kube 모듈이 kubeconfig 를 못 찾아 localhost:8080 으로 갑니다. 로그 중간의 ...ignoring 이 붙은 fatal 은 정상입니다.

로그 끝에서 읽는 것

/root/ks/install/run1.json 에 첫 실행의 ok, changed, failed(PLAY RECAP 의 node1 값), elapsed_sec(TASKS RECAP 머리에 찍힌 누적 시간을 초로, 정수), slowest_task(TASKS RECAP 목록 맨 위 작업 이름, 역할 접두사 포함 그대로), node_uid(kubectl get node node1 의 metadata.uid)를 적으세요.

ansible.cfg 가 켜 둔 profile_tasks 콜백이 로그 끝에 TASKS RECAP 을 붙입니다. 첫 줄의 마지막 시간이 전체 누적 시간이고, 아래 목록이 오래 걸린 순서입니다. 노드 UID 는 다음 단계에서 '다시 돌려도 같은 노드인가' 를 확인하는 기준이 됩니다.

한 번 더 돌린다

같은 명령을 한 번 더 돌려 출력 전체를 /root/ks/logs/cluster-2.log 에 남기세요. 두 번째도 failed=0 이어야 하고, 노드 UID 가 3단계에서 적은 값과 같아야 합니다(클러스터를 새로 만들지 않았다는 뜻).

kubespray 는 이미 세운 클러스터에 cluster.yml 을 다시 돌려도 되게 만들어져 있습니다 — 노드를 더하거나 설정을 바꿀 때 같은 플레이북을 다시 돌리는 것이 기본 운영 방법입니다. 두 번째 실행은 받을 것이 캐시에 있어 첫 번째보다 빠릅니다.

멱등인데 changed 가 0 이 아니다

두 번째 실행에서 changed: [node1] 을 낸 작업의 이름을 로그의 TASK [...](핸들러는 RUNNING HANDLER [...]) 괄호 안 그대로(역할 접두사 포함) 한 줄에 하나씩 /root/ks/install/changed.txt 에 적으세요. 그리고 /root/ks/install/run2.json 에 두 번째 실행의 ok, changed, failed, elapsed_sec 을 적습니다.

멱등(idempotent)은 '몇 번 돌려도 결과 상태가 같다' 이지 '아무 작업도 changed 를 내지 않는다' 가 아닙니다. 매번 무언가를 실행하는 command·shell 작업이나, 값을 비교하지 않고 다시 쓰는 작업이 changed 를 냅니다. 로그에서 changed: [node1] 줄 바로 위의 가장 가까운 TASK(또는 RUNNING HANDLER) 머리가 그 작업입니다.

값 하나를 바꾸고 그 부분만 다시

/root/ks/inventory/lab/group_vars/all/containerd.ymlcontainerd_max_container_log_line_size: 32768 을 더하고, cluster.yml --tags containerd 로 컨테이너 런타임 부분만 다시 돌려 출력 전체를 /root/ks/logs/containerd.log 에 남기세요. 끝나면 /etc/containerd/config.tomlmax_container_log_line_size 가 32768 이고 containerd 가 새 설정으로 다시 떠 있어야 하며, 노드는 여전히 Ready 여야 합니다.

태그를 주면 그 태그가 붙은 작업(과 always 태그의 준비 작업)만 돕니다. 어떤 태그가 있는지는 kubespray 문서의 태그 표나 --list-tags 로 봅니다. 설정 파일만 바뀌고 데몬이 다시 뜨지 않으면 옛 값으로 계속 돕니다 — 역할의 핸들러가 재시작을 맡습니다. 문서가 경고하듯 태그는 무엇이 도는지 확실히 알 때만 씁니다.

설치 보고서

/root/ks/install/report.jsonkubespray(태그), server_version(API 서버의 gitVersion), first_run_sec, second_run_sec(두 실행의 누적 시간, 정수), first_changed, second_changed, tags_run_changed(6단계 실행의 changed), containerd_restarted(6단계로 containerd 가 다시 떴는가, 불리언)를 적으세요.

앞 단계 기록과 로그 세 개, 그리고 지금의 클러스터에서 모두 다시 계산할 수 있는 값입니다. 채점기도 같은 곳에서 다시 계산해 대조합니다. containerd 가 다시 떴는지는 containerd.log 에 재시작 핸들러가 changed 로 찍혔는지로 판단합니다.