Building clusters with Kubespray and Terraform
Run cluster.yml twice, get the same cluster
한국어 원문으로 표시합니다.
목표
준비된 인벤토리로 cluster.yml 을 돌려 VM 한 대(4 vCPU · 4 GiB)에 쿠버네티스 1.35.8 을 세웁니다. 같은 플레이북을 한 번 더 돌려
무엇이 다시 바뀌는지 보고, 값 하나를 바꿔 태그로 그 부분만 다시 적용합니다.
왜 중요한가
kubespray 의 운영 방식은 "선언을 고치고 같은 플레이북을 다시 돌린다" 입니다. 노드를 더할 때도, 설정을 바꿀 때도 같은 cluster.yml 을 돌립니다. 그래서 두 번째 실행이 무엇을 바꾸는지 알아야 합니다 — 멱등이라는 말은 changed 가 0 이라는 뜻이 아니고, 매번 바뀌는 작업이 무엇인지 알아야 "이번 실행이 의도한 것만 바꿨는가" 를 판단할 수 있습니다. 긴 플레이북의 로그를 끝에서부터 읽는 법(PLAY RECAP 과 TASKS RECAP)도 여기서 익힙니다. 이 실습은 설치 두 번과 부분 실행 한 번에 모두 약 15분을 기다립니다. 세션이 60분이니 여유가 모자라면 연장하세요. VM 이 끝나면 클러스터도 사라집니다.
단계
/opt/ks/kubespray에서ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml --list-tasks로 플레이 목록을 보고/root/ks/install/plan.json에play_count(play 개수),etcd_play("Install etcd" play 의 번호),cni_play("Invoke kubeadm and install a CNI" 의 번호),apps_play("Install Kubernetes apps" 의 번호)를 숫자로 적으세요./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 여야 합니다./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)를 적으세요.- 같은 명령을 한 번 더 돌려 출력 전체를
/root/ks/logs/cluster-2.log에 남기세요. 두 번째도failed=0이어야 하고, 노드 UID 가 3단계에서 적은 값과 같아야 합니다(클러스터를 새로 만들지 않았다는 뜻). - 두 번째 실행에서
changed: [node1]을 낸 작업의 이름을 로그의TASK [...](핸들러는RUNNING HANDLER [...]) 괄호 안 그대로(역할 접두사 포함) 한 줄에 하나씩/root/ks/install/changed.txt에 적으세요. 그리고/root/ks/install/run2.json에 두 번째 실행의ok,changed,failed,elapsed_sec을 적습니다. /root/ks/inventory/lab/group_vars/all/containerd.yml에containerd_max_container_log_line_size: 32768을 더하고,cluster.yml --tags containerd로 컨테이너 런타임 부분만 다시 돌려 출력 전체를/root/ks/logs/containerd.log에 남기세요. 끝나면/etc/containerd/config.toml의max_container_log_line_size가 32768 이고 containerd 가 새 설정으로 다시 떠 있어야 하며, 노드는 여전히 Ready 여야 합니다./root/ks/install/report.json에kubespray(태그),server_version(API 서버의 gitVersion),first_run_sec,second_run_sec(두 실행의 누적 시간, 정수),first_changed,second_changed,tags_run_changed(6단계 실행의 changed),containerd_restarted(6단계로 containerd 가 다시 떴는가, 불리언)를 적으세요.
참고
- kubespray v2.32.0 이
/opt/ks/kubespray에, 인벤토리가/root/ks/inventory/lab/inventory.ini에 준비돼 있습니다(노드 한 대, kube_version 1.35.8). 플레이북은/opt/ks/kubespray에서 돌립니다. - 설치 중 받는 파일은
/tmp/releases에, 이미지는 containerd 저장소에 쌓입니다. 두 번째 실행이 빠른 이유입니다. - 흔한 실수: 로그의
fatal:을 보고 실패로 판단하는 것....ignoring이 붙은 fatal 은 kubespray 가 일부러 넘기는 확인 작업입니다. 판정은 PLAY RECAP 의 failed 로 합니다. - 흔한 실수: 첫 설치가 도중에 멈춘 뒤 cluster.yml 만 다시 돌리는 것. CNI 앞에서 멈췄다면 두 번째 실행이 컨트롤 플레인 Ready 를 기다리다 실패합니다. 그때는 reset.yml 로 지우고 처음부터 세웁니다(7모듈).
- 문서: Kubespray — Getting started · Kubespray — Ansible tags
돌리기 전에 순서를 본다
/opt/ks/kubespray 에서 ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml --list-tasks 로 플레이 목록을 보고 /root/ks/install/plan.json 에 play_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.yml 에 containerd_max_container_log_line_size: 32768 을 더하고, cluster.yml --tags containerd 로 컨테이너 런타임 부분만 다시 돌려 출력 전체를 /root/ks/logs/containerd.log 에 남기세요. 끝나면 /etc/containerd/config.toml 의 max_container_log_line_size 가 32768 이고 containerd 가 새 설정으로 다시 떠 있어야 하며, 노드는 여전히 Ready 여야 합니다.
태그를 주면 그 태그가 붙은 작업(과 always 태그의 준비 작업)만 돕니다. 어떤 태그가 있는지는 kubespray 문서의 태그 표나 --list-tags 로 봅니다. 설정 파일만 바뀌고 데몬이 다시 뜨지 않으면 옛 값으로 계속 돕니다 — 역할의 핸들러가 재시작을 맡습니다. 문서가 경고하듯 태그는 무엇이 도는지 확실히 알 때만 씁니다.
설치 보고서
/root/ks/install/report.json 에 kubespray(태그), 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 로 찍혔는지로 판단합니다.