LabHub
开始
学习 学习路径 课程

用 Kubespray 与 Terraform 搭建集群

要证据,不要绿灯 — 逐层验证安装结果

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

kubespray 로 세운 클러스터를 노드 → 코어 파드 → DNS → 인증서 → 남은 파일 → 실제 워크로드 순서로 확인하고, "다음에 무엇을 언제 해야 하는가" 를 보고서 한 장으로 남깁니다.

왜 중요한가

PLAY RECAP 의 failed=0 은 "플레이북이 끝까지 돌았다" 는 뜻이지 "클러스터를 쓸 수 있다" 는 뜻이 아닙니다. kubespray 는 kubeadm 위에 몇 가지를 자기 방식으로 얹습니다 — etcd 를 호스트 서비스로 따로 세우고, 파드 DNS 앞에 노드 로컬 캐시를 두고, 인증서 디렉터리를 옮깁니다. 그래서 kubeadm 으로 직접 세운 클러스터를 점검하던 습관 그대로 보면 etcd 인증서를 빠뜨리거나, CoreDNS 만 보고 DNS 가 된다고 판단하게 됩니다. 층마다 한 번씩 직접 확인하는 순서를 손에 익혀 두면 설치 뒤에도, 업그레이드 뒤에도 같은 순서로 점검할 수 있습니다.

단계

  1. /opt/ks/kubespray 에서 ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml 을 돌려 출력 전체를 /root/ks/logs/cluster-1.log 에 남기세요(약 7분). PLAY RECAP 의 node1 이 failed=0 이어야 합니다.
  2. /root/ks/verify/node.jsonready(Ready 조건 status 문자열), kubelet_version, container_runtime(nodeInfo.containerRuntimeVersion), internal_ip, roles(node-role.kubernetes.io/ 로 시작하는 라벨의 역할 이름 정렬 배열), taints(키:효과 문자열 배열, 없으면 빈 배열)를 적으세요.
  3. kube-system 의 파드를 보고 /root/ks/verify/pods.jsonapps(각 파드의 k8s-app 라벨, 없으면 component 라벨 값을 모은 중복 없는 정렬 배열), not_ready(Ready 가 아닌 파드 이름 배열, 없으면 빈 배열), etcd_is_pod(etcd 가 파드로 떠 있는가, 불리언), etcd_unit(etcd 를 띄운 systemd 유닛 이름, .service 포함)를 적으세요.
  4. /root/ks/verify/dns.jsonkubelet_cluster_dns(/var/lib/kubelet/config.yaml 의 clusterDNS 첫 값), coredns_service_ip(kube-system 의 CoreDNS 서비스 ClusterIP), answer_via_nodelocal, answer_via_coredns(노드에서 두 주소에 각각 kubernetes.default.svc.cluster.local 을 물은 A 레코드)를 적으세요. 두 답은 kubernetes 서비스의 ClusterIP 와 같아야 합니다.
  5. /root/ks/verify/certs.jsonapiserver_not_after, ca_not_after(/etc/kubernetes/ssl 의 apiserver.crt·ca.crt), etcd_member_not_after(/etc/ssl/etcd/ssl/member-node1.pem)를 UTC 2026-01-01T00:00:00Z 형식으로, apiserver_valid_days, etcd_member_valid_days(각 인증서 notBefore 부터 notAfter 까지 일수, 정수)와 kubeadm_lists_etcd(kubeadm certs check-expiration 출력에 etcd 인증서 줄이 있는가, 불리언)를 적으세요.
  6. /root/ks/verify/files.jsonkubeadm_config_version(/etc/kubernetes/kubeadm-config.yaml 의 ClusterConfiguration kubernetesVersion), etcd_data_dir(/etc/etcd.env 의 ETCD_DATA_DIR), kubeconfig_server(/root/.kube/config 의 server 주소), containerd_version(containerd --version 의 세 번째 칸), releases_mb(/tmp/releases 의 크기, MiB 정수 — du -sm)을 적으세요.
  7. default 네임스페이스에 Deployment web(이미지 registry.k8s.io/e2e-test-images/agnhost:2.59, 인자 netexec --http-port=8080, 레플리카 2)과 같은 이름의 ClusterIP Service(포트 80 → 8080)를 만드세요. 두 파드가 Ready 가 되면 노드에서 curl http://<web 서비스 IP>/hostname 을 여러 번 불러 두 파드 이름이 모두 돌아오는 것을 확인하고, 그 두 이름을 정렬해 /root/ks/verify/smoke.txt 에 한 줄에 하나씩 적으세요.
  8. /root/ks/verify/report.jsonnode_ready(불리언), core_apps(3단계 apps 개수, 숫자), dns_ok(4단계 두 답이 kubernetes 서비스 IP 와 같은가, 불리언), first_cert_to_expire(kubeadm 잎 인증서와 etcd member 인증서 가운데 먼저 만료되는 쪽: "kubeadm" 또는 "etcd"), days_until_first_expiry(그 인증서가 만료될 때까지 남은 일수, 정수), smoke_pods(7단계에서 응답한 파드 수, 숫자)를 적으세요.

참고

검증할 클러스터를 세운다

/opt/ks/kubespray 에서 ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml 을 돌려 출력 전체를 /root/ks/logs/cluster-1.log 에 남기세요(약 7분). PLAY RECAP 의 node1 이 failed=0 이어야 합니다.

3모듈에서 한 설치와 같습니다. 콘솔이 끊겨도 계속 돌게 systemd-run 이나 tmux 로 띄우고 HOME=/root 를 넘기세요. 기다리는 동안 이 실습의 다음 단계들을 읽어 두면 좋습니다.

노드 — Ready 와 테인트

/root/ks/verify/node.jsonready(Ready 조건 status 문자열), kubelet_version, container_runtime(nodeInfo.containerRuntimeVersion), internal_ip, roles(node-role.kubernetes.io/ 로 시작하는 라벨의 역할 이름 정렬 배열), taints(키:효과 문자열 배열, 없으면 빈 배열)를 적으세요.

kubeadm 은 컨트롤 플레인 노드에 NoSchedule 테인트를 붙입니다. 그런데 이 노드는 인벤토리의 kube_node 에도 들어 있습니다 — kubespray 가 그 사실을 보고 무엇을 했는지 테인트 목록으로 확인하세요. 역할 이름은 라벨 키의 마지막 조각입니다.

코어 파드 — 무엇이 떠 있고 무엇이 없나

kube-system 의 파드를 보고 /root/ks/verify/pods.jsonapps(각 파드의 k8s-app 라벨, 없으면 component 라벨 값을 모은 중복 없는 정렬 배열), not_ready(Ready 가 아닌 파드 이름 배열, 없으면 빈 배열), etcd_is_pod(etcd 가 파드로 떠 있는가, 불리언), etcd_unit(etcd 를 띄운 systemd 유닛 이름, .service 포함)를 적으세요.

kubeadm 으로 직접 세운 클러스터와 다른 점이 여기서 보입니다. kubespray 의 기본 etcd 배치(etcd_deployment_type)는 무엇인지 group_vars/all/etcd.yml 에서 찾고, systemctl list-units 로 실제 유닛을 확인하세요.

DNS — 파드가 묻는 곳은 CoreDNS 가 아니다

/root/ks/verify/dns.jsonkubelet_cluster_dns(/var/lib/kubelet/config.yaml 의 clusterDNS 첫 값), coredns_service_ip(kube-system 의 CoreDNS 서비스 ClusterIP), answer_via_nodelocal, answer_via_coredns(노드에서 두 주소에 각각 kubernetes.default.svc.cluster.local 을 물은 A 레코드)를 적으세요. 두 답은 kubernetes 서비스의 ClusterIP 와 같아야 합니다.

kubespray 는 기본으로 nodelocaldns(enable_nodelocaldns: true)를 켭니다. 노드마다 링크 로컬 주소에서 캐시 DNS 가 돌고, kubelet 은 파드에게 그 주소를 알려 줍니다. CoreDNS 서비스의 이름은 kubeadm 기본값과 다를 수 있으니 서비스 목록에서 셀렉터로 찾으세요. dig +short @<주소> <이름> 이 답만 돌려줍니다.

인증서 — 두 갈래의 만료일

/root/ks/verify/certs.jsonapiserver_not_after, ca_not_after(/etc/kubernetes/ssl 의 apiserver.crt·ca.crt), etcd_member_not_after(/etc/ssl/etcd/ssl/member-node1.pem)를 UTC 2026-01-01T00:00:00Z 형식으로, apiserver_valid_days, etcd_member_valid_days(각 인증서 notBefore 부터 notAfter 까지 일수, 정수)와 kubeadm_lists_etcd(kubeadm certs check-expiration 출력에 etcd 인증서 줄이 있는가, 불리언)를 적으세요.

kubespray 는 kubeadm 의 인증서 디렉터리를 /etc/kubernetes/ssl 로 둡니다(kubeadm 기본은 pki). etcd 를 호스트 서비스로 띄우는 기본 배치에서는 etcd 인증서를 kubeadm 이 아니라 kubespray 가 openssl 로 만들고, 그 기간은 kubespray_defaults 의 certificates_duration 입니다. 한쪽만 보고 '인증서 만료 점검 끝' 이라고 하면 안 되는 이유가 여기 있습니다.

kubespray 가 남긴 것

/root/ks/verify/files.jsonkubeadm_config_version(/etc/kubernetes/kubeadm-config.yaml 의 ClusterConfiguration kubernetesVersion), etcd_data_dir(/etc/etcd.env 의 ETCD_DATA_DIR), kubeconfig_server(/root/.kube/config 의 server 주소), containerd_version(containerd --version 의 세 번째 칸), releases_mb(/tmp/releases 의 크기, MiB 정수 — du -sm)을 적으세요.

kubespray 는 kubeadm 을 부를 때 쓴 설정 파일과 etcd 환경 파일을 노드에 남깁니다. 설치가 어떤 값으로 됐는지 나중에 확인하는 1차 자료입니다. /tmp/releases 는 kubespray 가 받은 바이너리 캐시(local_release_dir)이고, 재설치나 업그레이드가 빠른 이유이기도 합니다.

마지막 증거는 워크로드다

default 네임스페이스에 Deployment web(이미지 registry.k8s.io/e2e-test-images/agnhost:2.59, 인자 netexec --http-port=8080, 레플리카 2)과 같은 이름의 ClusterIP Service(포트 80 → 8080)를 만드세요. 두 파드가 Ready 가 되면 노드에서 curl http://<web 서비스 IP>/hostname 을 여러 번 불러 두 파드 이름이 모두 돌아오는 것을 확인하고, 그 두 이름을 정렬해 /root/ks/verify/smoke.txt 에 한 줄에 하나씩 적으세요.

이 단계가 통과하면 이미지 받기(containerd·레지스트리 이그레스), 스케줄링(테인트), 파드 네트워크(calico), 서비스(kube-proxy)가 한 번에 확인됩니다. 초록불 대신 실제 요청을 보내는 이유입니다. agnhost 의 netexec 는 /hostname 에 파드 이름을 돌려줍니다.

검증 보고서

/root/ks/verify/report.jsonnode_ready(불리언), core_apps(3단계 apps 개수, 숫자), dns_ok(4단계 두 답이 kubernetes 서비스 IP 와 같은가, 불리언), first_cert_to_expire(kubeadm 잎 인증서와 etcd member 인증서 가운데 먼저 만료되는 쪽: "kubeadm" 또는 "etcd"), days_until_first_expiry(그 인증서가 만료될 때까지 남은 일수, 정수), smoke_pods(7단계에서 응답한 파드 수, 숫자)를 적으세요.

앞 단계의 기록과 지금의 클러스터에서 모두 다시 계산할 수 있습니다. 남은 일수는 지금 시각에서 notAfter 까지를 내림한 정수로 계산합니다. 이 보고서의 목적은 '다음에 무엇을 언제 해야 하는가' 를 한 줄로 남기는 것입니다.