Kubernetes

[K8S Deploy 4주차] kubespray를 이용한 K8S 클러스터 구성

yu3papa 2026. 1. 31. 12:29

GitHub 공식 레포지토리(https://github.com/kubernetes-sigs/kubespray) 에서 kubespray 를 아래와 같이 요약하고 있습니다.

Deploy a Production Ready Kubernetes Cluster

 

그래서 kubespray 는 "kubespray는 Ansible 기반으로 쿠버네티스(Kubernetes) 클러스터를 자동으로 설치·업그레이드·관리하기 위한 오픈소스 배포 도구" 로 요약할 수 있습니다.

내부적으로는 Ansible playbook 으로 구성되어 있고 이번 블로그에서는 playbook 을 분석해 보겠습니다.

 

특히 폐쇄망(Air-Gap) 환경에서 K8S 클러스터 설치에 kubespray를 이용하면 효과적입니다.

 

kubespray 를 이용하여 K8S 클러스터 생성 부터 운영 전반을 지원할 수 있습니다.

  • 신규 클러스터 생성
  • (컨트롤 플레인) 클러스터 업그레이드
  • 클러스터 스케일링
  • 노드 관리 - 노드 추가, 노드 제거
  • 클러스터 재설정
  • 설정 관리
  • 백업/복구, 업그레이드 시 etcd 스냅샷 수행

kubespray 설치 사전 요건

  • Ansible v2.14+, Jinja 2.11+ and python-netaddr is installed on the machine that will run Ansible commands
  • Ansible 2.17.3 이상 & Python 3.10 ~ 3.12
  • Spec : Control Plane (Memory 2 GB), Worker Node (Memory 1 GB)
  • Linux Kernel Requirements : 5.8+ 이상 권장
  • Rocky Linux [9, 10](experimental in 10: see [Rocky Linux 10 notes]

실습환경 배포

# 실습용 디렉터리 생성
mkdir k8s-kubespary
cd k8s-kubespary

# 파일 다운로드
https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-kubespary/Vagrantfile
https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-kubespary/init_cfg.sh

# 실습 환경 배포
vagrant up

vagrant status

 

참고) Vagrantfile, init_cfg.sh

더보기
더보기

Vagrantfile

# Base Image  https://portal.cloud.hashicorp.com/vagrant/discover/bento/rockylinux-10.0
BOX_IMAGE = "bento/rockylinux-10.0"
BOX_VERSION = "202510.26.0"

Vagrant.configure("2") do |config|

# ControlPlane Nodes 
    config.vm.define "k8s-ctr" do |subconfig|
      subconfig.vm.box = BOX_IMAGE
      subconfig.vm.box_version = BOX_VERSION
      subconfig.vm.provider "virtualbox" do |vb|
        vb.customize ["modifyvm", :id, "--groups", "/Kubespray-Lab"]
        vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]
        vb.name = "k8s-ctr"
        vb.cpus = 4
        vb.memory = 4096
        vb.linked_clone = true
      end
      subconfig.vm.host_name = "k8s-ctr"
      subconfig.vm.network "private_network", ip: "192.168.10.10"
      subconfig.vm.network "forwarded_port", guest: 22, host: "60100", auto_correct: true, id: "ssh"
      subconfig.vm.synced_folder "./", "/vagrant", disabled: true
      subconfig.vm.provision "shell", path: "init_cfg.sh"
    end

end

 

init_cfg.sh

#!/usr/bin/env bash

echo ">>>> Initial Config Start <<<<"


echo "[TASK 1] Change Timezone and Enable NTP"
timedatectl set-local-rtc 0
timedatectl set-timezone Asia/Seoul


echo "[TASK 2] Disable firewalld and selinux"
systemctl disable --now firewalld >/dev/null 2>&1
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config


echo "[TASK 3] Disable and turn off SWAP & Delete swap partitions"
swapoff -a
sed -i '/swap/d' /etc/fstab
sfdisk --delete /dev/sda 2 >/dev/null 2>&1
partprobe /dev/sda >/dev/null 2>&1


echo "[TASK 4] Config kernel & module"
cat << EOF > /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
modprobe overlay >/dev/null 2>&1
modprobe br_netfilter >/dev/null 2>&1

cat << EOF >/etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sysctl --system >/dev/null 2>&1


echo "[TASK 5] Setting Local DNS Using Hosts file"
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
cat << EOF >> /etc/hosts
192.168.10.10 k8s-ctr
EOF


echo "[TASK 6] Delete default routing - enp0s9 NIC" # setenforce 0 설정 필요
nmcli connection modify enp0s9 ipv4.never-default yes
nmcli connection up enp0s9 >/dev/null 2>&1


echo "sudo su -" >> /home/vagrant/.bashrc

echo ">>>> Initial Config End <<<<"

사전 설정 수행 & kubespray git clone

"vagrant ssh" 명령을 수행한 후 아래 초기 작업(kubespray 사전조건)을 진행합니다.

# Linux Kernel Requirements : 5.8+ 이상 권장
uname -a
Linux k8s-ctr 6.12.0-55.39.1.el10_0.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Oct 15 14:24:00 UTC 2025 x86_64 GNU/Linux

# Python : 3.10 ~ 3.12 : (참고) bento/rockylinux-9 경우 3.9
which python  && python -V
which python3 && python3 -V
3.12.9

# pip , git 설치
dnf install -y python3-pip git
which pip  && pip -V
which pip3 && pip3 -V
pip 23.3.2 from /usr/lib/python3.12/site-packages/pip (python 3.12)

# /etc/hosts 확인
ip -br -c -4 addr
lo               UNKNOWN        127.0.0.1/8
enp0s3           UP             10.0.2.15/24
enp0s8           UP             192.168.10.10/24

cat /etc/hosts
# Loopback entries; do not change.
# For historical reasons, localhost precedes localhost.localdomain:
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
# See hosts(5) for proper format and other examples:
# 192.168.1.10 foo.example.org foo
# 192.168.1.13 bar.example.org bar
192.168.10.10 k8s-ctr

# SSH 접속을 위한 설정 <-- ansible 을 이용하렴 ssh 접속이 가능해야 함
echo "root:qwe123" | chpasswd

cat << EOF >> /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
EOF

systemctl restart sshd

# Setting SSH Key
ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa

# ssh-copy-id
ssh-copy-id -o StrictHostKeyChecking=no root@192.168.10.10
root@192.168.10.10's password: qwe123

# ssh 접속 확인 : IP, hostname
ssh root@192.168.10.10 hostname
ssh -o StrictHostKeyChecking=no root@k8s-ctr hostname
ssh root@k8s-ctr hostname
k8s-ctr

# Clone Kubespray Repository
git clone -b v2.29.1 https://github.com/kubernetes-sigs/kubespray.git /root/kubespray
cd /root/kubespray

# (옵션) IDE에서 VM SSH 접속(root/qwe123)해서 편집 창 열기


# 최상단 plybook 확인 -> 각각 import_playbook 확인

ls -l *.yml
-rw-r--r--. 1 root root  88 Jan 24 20:55 cluster.yml    # ansible.builtin.import_playbook: playbooks/cluster.yml
-rw-r--r--. 1 root root  30 Jan 24 20:55 _config.yml
-rw-r--r--. 1 root root 747 Jan 24 20:55 galaxy.yml
-rw-r--r--. 1 root root 105 Jan 24 20:55 recover-control-plane.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 remove-node.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 remove_node.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 reset.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 scale.yml      # ansible.builtin.import_playbook: playbooks/scale.yml
-rw-r--r--. 1 root root  93 Jan 24 20:55 upgrade-cluster.yml
-rw-r--r--. 1 root root  93 Jan 24 20:55 upgrade_cluster.yml



tree -L 2
...
├── inventory
│   ├── local
│   └── sample
...
├── playbooks
│   ├── ansible_version.yml
│   ├── boilerplate.yml
│   ├── cluster.yml*
│   ├── facts.yml
│   ├── install_etcd.yml
│   ├── internal_facts.yml
│   ├── recover_control_plane.yml
│   ├── remove_node.yml
│   ├── reset.yml
│   ├── scale.yml
│   └── upgrade_cluster.yml
...
├── roles
│   ├── adduser
│   ├── bastion-ssh-config
│   ├── bootstrap-os
│   ├── bootstrap_os
│   ├── container-engine
│   ├── download
│   ├── dynamic_groups
│   ├── etcd
│   ├── etcdctl_etcdutl
│   ├── etcd_defaults
│   ├── helm-apps
│   ├── kubernetes
│   ├── kubernetes-apps
│   ├── kubespray-defaults
│   ├── kubespray_defaults
│   ├── network_facts
│   ├── network_plugin
│   ├── recover_control_plane
│   ├── remove-node
│   ├── remove_node
│   ├── reset
│   ├── system_packages
│   ├── upgrade
│   ├── validate_inventory
│   └── win_nodes
...

# Install Python Dependencies
cat requirements.txt
ansible==10.7.0
# Needed for community.crypto module
cryptography==46.0.3
# Needed for jinja2 json_query templating
jmespath==1.0.1
# Needed for ansible.utils.ipaddr
netaddr==1.3.0

pip3 install -r /root/kubespray/requirements.txt
Successfully installed MarkupSafe-3.0.3 ansible-10.7.0 ansible-core-2.17.14 cffi-2.0.0 cryptography-46.0.2 jinja2-3.1.6 jmespath-1.0.1 netaddr-1.3.0 pycparser-3.0 resolvelib-1.0.1

# ansible 버전 확인 : Ansible 2.17.3 이상
which ansible
ansible --version
ansible [core 2.17.14]
  config file = /root/kubespray/ansible.cfg
  ...
  python version = 3.12.9 (main, Aug 14 2025, 00:00:00) [GCC 14.2.1 20250110 (Red Hat 14.2.1-7)] (/usr/bin/python3)
  jinja version = 3.1.6
  libyaml = True

# pip list 확인
pip list
Package                   Version
------------------------- -----------
ansible                   10.7.0
ansible-core              2.17.14
...
Jinja2                    3.1.6
jmespath                  1.0.1
...
netaddr                   1.3.0
...

# 해당 폴더에서 ansible-playbook 실행 시 적용되는 ansible.cfg --> k8s 를 관리하는 편리한 옵션 설정이 들어가 있음
cat ansible.cfg
[ssh_connection] # 통신 속도 및 안정성 최적화
pipelining=True  # SSH 세션을 여러 번 열지 않고 하나의 세션에서 여러 명령을 한꺼번에 실행
ssh_args = -o ControlMaster=auto -o ControlPersist=30m -o ConnectionAttempts=100 -o UserKnownHostsFile=/dev/null
## ControlMaster=auto -o ControlPersist=30m: 한 번 연결된 SSH 커넥션을 30분 동안 유지합니다. 매번 로그인할 필요가 없어 성능이 향상됩니다.
## ConnectionAttempts=100: 네트워크 불안정으로 연결 실패 시 100번까지 재시도합니다.
## UserKnownHostsFile=/dev/null: 접속 대상의 지문(fingerprint)을 저장하지 않아 관리가 편해집니다.
#control_path = ~/.ssh/ansible-%%r@%%h:%%p

[defaults]
https://github.com/ansible/ansible/issues/56930 (to ignore group names with - and .)
force_valid_group_names = ignore  # Ansible은 원래 그룹 이름에 -나 . 사용을 제한하지만, 쿠버네티스 리소스 명칭 규칙상 이를 허용하도록 설정
host_key_checking=False # 새 서버 접속 시 "Are you sure you want to continue connecting?"이라는 확인 창이 뜨지 않게 합니다.
gathering = smart         # 대상 서버의 정보(Fact)를 한 번만 수집하고 /tmp에 JSON 파일로 저장합니다. (아래 설명 이어서)
fact_caching = jsonfile   # 재실행 시 서버 정보를 다시 수집하지 않아 시간이 단축됩니다. 86400(24시간) 동안 캐시를 유지합니다.
fact_caching_connection = /tmp
fact_caching_timeout = 86400
timeout = 300
stdout_callback = default
display_skipped_hosts = no
library = ./library
callbacks_enabled = profile_tasks # 각 Task가 실행되는 데 걸리는 시간을 표시해 줍니다. 어떤 단계에서 병목이 생기는지 확인할 때 매우 유용합니다.
roles_path = roles:$VIRTUAL_ENV/usr/local/share/kubespray/roles:$VIRTUAL_ENV/usr/local/share/ansible/roles:/usr/share/kubespray/roles
deprecation_warnings=False
inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo, .creds, .gpg # 백업용이나 임시 파일을 인벤토리로 인식하여 에러가 발생하는 것을 방지합니다.

[inventory]
ignore_patterns = artifacts, credentials # 배포 결과물(artifacts)이나 중요 정보(credentials) 폴더 내의 파일을 인벤토리 스캔 대상에서 제외합니다.

 

Kubespary 를 통한 k8s 배포 (5분 정도 소요) : 목표 환경을 위한 파라미터 설정

 - [Variables](https://github.com/kubernetes-sigs/kubespray/blob/master/docs/ansible/vars.md)

kubespray 를 이용하여 Rocky Linux 9 버전의 1대의 머신에 k8s 클러스터를 생성해 보겠습니다.

# inventory 디렉터리 복사
cp -rfp /root/kubespray/inventory/sample /root/kubespray/inventory/mycluster
tree inventory/mycluster/
inventory/mycluster/
├── group_vars
│   ├── all
│   │   ├── all.yml
│   │   ├── aws.yml
│   │   ├── azure.yml
│   │   ├── containerd.yml
│   │   ├── coreos.yml
│   │   ├── cri-o.yml
│   │   ├── docker.yml
│   │   ├── etcd.yml
│   │   ├── gcp.yml
│   │   ├── hcloud.yml
│   │   ├── huaweicloud.yml
│   │   ├── oci.yml
│   │   ├── offline.yml
│   │   ├── openstack.yml
│   │   ├── upcloud.yml
│   │   └── vsphere.yml
│   └── k8s_cluster
│       ├── addons.yml
│       ├── k8s-cluster.yml
│       ├── k8s-net-calico.yml
│       ├── k8s-net-cilium.yml
│       ├── k8s-net-custom-cni.yml
│       ├── k8s-net-flannel.yml
│       ├── k8s-net-kube-ovn.yml
│       ├── k8s-net-kube-router.yml
│       ├── k8s-net-macvlan.yml
│       └── kube_control_plane.yml
└── inventory.ini

# inventory.ini 작성
cat << EOF > /root/kubespray/inventory/mycluster/inventory.ini
k8s-ctr ansible_host=192.168.10.10 ip=192.168.10.10

[kube_control_plane]
k8s-ctr

[etcd:children]
kube_control_plane

[kube_node]
k8s-ctr
EOF

# https://github.com/kubernetes-sigs/kubespray/blob/master/docs/ansible/vars.md
## <your-favorite-editor> inventory/mycluster/group_vars/all.yml # for every node, including etcd
grep "^[^#]" inventory/mycluster/group_vars/all/all.yml
---
bin_dir: /usr/local/bin
loadbalancer_apiserver_port: 6443
loadbalancer_apiserver_healthcheck_port: 8081
no_proxy_exclude_workers: false
kube_webhook_token_auth: false
kube_webhook_token_auth_url_skip_tls_verify: false
ntp_enabled: false
ntp_manage_config: false
ntp_servers:
  - "0.pool.ntp.org iburst"
  - "1.pool.ntp.org iburst"
  - "2.pool.ntp.org iburst"
  - "3.pool.ntp.org iburst"
unsafe_show_logs: false
allow_unsupported_distribution_setup: false

## <your-favorite-editor> inventory/mycluster/group_vars/k8s_cluster.yml # for every node in the cluster (not etcd when it's separate)
grep "^[^#]" inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
---
kube_config_dir: /etc/kubernetes
kube_script_dir: "{{ bin_dir }}/kubernetes-scripts"
kube_manifest_dir: "{{ kube_config_dir }}/manifests"
kube_cert_dir: "{{ kube_config_dir }}/ssl"
kube_token_dir: "{{ kube_config_dir }}/tokens"
kube_api_anonymous_auth: true
local_release_dir: "/tmp/releases"
retry_stagger: 5
kube_owner: kube
kube_cert_group: kube-cert
kube_log_level: 2
credentials_dir: "{{ inventory_dir }}/credentials"
kube_network_plugin: calico
kube_network_plugin_multus: false
kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18
kube_network_node_prefix: 24
kube_service_addresses_ipv6: fd85:ee78:d8a6:8607::1000/116
kube_pods_subnet_ipv6: fd85:ee78:d8a6:8607::1:0000/112
kube_network_node_prefix_ipv6: 120
kube_apiserver_ip: "{{ kube_service_subnets.split(',') | first | ansible.utils.ipaddr('net') | ansible.utils.ipaddr(1) | ansible.utils.ipaddr('address') }}"
kube_apiserver_port: 6443  # (https)
kube_proxy_mode: ipvs
kube_proxy_strict_arp: false
kube_proxy_nodeport_addresses: >-
  {%- if kube_proxy_nodeport_addresses_cidr is defined -%}
  [{{ kube_proxy_nodeport_addresses_cidr }}]
  {%- else -%}
  []
  {%- endif -%}
kube_encrypt_secret_data: false
cluster_name: cluster.local
ndots: 2
dns_mode: coredns
enable_nodelocaldns: true
enable_nodelocaldns_secondary: false
nodelocaldns_ip: 169.254.25.10
nodelocaldns_health_port: 9254
nodelocaldns_second_health_port: 9256
nodelocaldns_bind_metrics_host_ip: false
nodelocaldns_secondary_skew_seconds: 5
enable_coredns_k8s_external: false
coredns_k8s_external_zone: k8s_external.local
enable_coredns_k8s_endpoint_pod_names: false
resolvconf_mode: host_resolvconf
deploy_netchecker: false
skydns_server: "{{ kube_service_subnets.split(',') | first | ansible.utils.ipaddr('net') | ansible.utils.ipaddr(3) | ansible.utils.ipaddr('address') }}"
skydns_server_secondary: "{{ kube_service_subnets.split(',') | first | ansible.utils.ipaddr('net') | ansible.utils.ipaddr(4) | ansible.utils.ipaddr('address') }}"
dns_domain: "{{ cluster_name }}"
container_manager: containerd
kata_containers_enabled: false
kubeadm_certificate_key: "{{ lookup('password', credentials_dir + '/kubeadm_certificate_key.creds length=64 chars=hexdigits') | lower }}"
k8s_image_pull_policy: IfNotPresent
kubernetes_audit: false
default_kubelet_config_dir: "{{ kube_config_dir }}/dynamic_kubelet_dir"
volume_cross_zone_attachment: false
persistent_volumes_enabled: false
event_ttl_duration: "1h0m0s"
auto_renew_certificates: false
# auto_renew_certificates_systemd_calendar: "Mon *-*-1,2,3,4,5,6,7 03:00:00"  # First Monday of each month
kubeadm_patches_dir: "{{ kube_config_dir }}/patches"
kubeadm_patches: []
remove_anonymous_access: false

# 테스트할 기능 관련 수정
sed -i 's|kube_network_plugin: calico|kube_network_plugin: flannel|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|kube_proxy_mode: ipvs|kube_proxy_mode: iptables|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|enable_nodelocaldns: true|enable_nodelocaldns: false|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|auto_renew_certificates: false|auto_renew_certificates: true|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|# auto_renew_certificates_systemd_calendar|auto_renew_certificates_systemd_calendar|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
grep -iE 'kube_network_plugin:|kube_proxy_mode|enable_nodelocaldns:|^auto_renew_certificates' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml


## flannel 설정 수정  inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
cat inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
echo "flannel_interface: enp0s9" >> inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
grep "^[^#]" inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml


## <your-favorite-editor> inventory/mycluster/group_vars/kube_control_plane.yml # for the control plane
cat inventory/mycluster/group_vars/k8s_cluster/kube_control_plane.yml 
# Reservation for control plane kubernetes components
# kube_memory_reserved: 512Mi
# kube_cpu_reserved: 200m
# kube_ephemeral_storage_reserved: 2Gi
# kube_pid_reserved: "1000"

# Reservation for control plane host system
# system_memory_reserved: 256Mi
# system_cpu_reserved: 250m
# system_ephemeral_storage_reserved: 2Gi
# system_pid_reserved: "1000"

## <your-favorite-editor> addons.yml
grep "^[^#]" inventory/mycluster/group_vars/k8s_cluster/addons.yml
---
helm_enabled: false
registry_enabled: false
metrics_server_enabled: false
local_path_provisioner_enabled: false
local_volume_provisioner_enabled: false
gateway_api_enabled: false
ingress_nginx_enabled: false
ingress_publish_status_address: ""
ingress_alb_enabled: false
cert_manager_enabled: false
metallb_enabled: false
metallb_speaker_enabled: "{{ metallb_enabled }}"
metallb_namespace: "metallb-system"
argocd_enabled: false
kube_vip_enabled: false
node_feature_discovery_enabled: false

# 테스트할 기능 관련 수정
sed -i 's|helm_enabled: false|helm_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
sed -i 's|metrics_server_enabled: false|metrics_server_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
sed -i 's|node_feature_discovery_enabled: false|node_feature_discovery_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
grep -iE 'helm_enabled:|metrics_server_enabled:|node_feature_discovery_enabled:' inventory/mycluster/group_vars/k8s_cluster/addons.yml


# etcd.yml : 파드가 아닌 systemd unit
grep "^[^#]" inventory/mycluster/group_vars/all/etcd.yml
---
etcd_data_dir: /var/lib/etcd
etcd_deployment_type: host

# containerd.yml 
cat inventory/mycluster/group_vars/all/containerd.yml
---
# Please see roles/container-engine/containerd/defaults/main.yml for more configuration options

# containerd_storage_dir: "/var/lib/containerd"
# containerd_state_dir: "/run/containerd"
# containerd_oom_score: 0

# containerd_default_runtime: "runc"
# containerd_snapshotter: "native"

# containerd_runc_runtime:
#   name: runc
#   type: "io.containerd.runc.v2"
#   engine: ""
...(생략)...

# 기본 환경 정보 출력 저장
ip addr | tee -a ip_addr-1.txt 
ss -tnlp | tee -a ss-1.txt
df -hT | tee -a df-1.txt
findmnt | tee -a findmnt-1.txt
sysctl -a | tee -a sysctl-1.txt

# 지원 버전 정보 확인
cat roles/kubespray_defaults/vars/main/checksums.yml | grep -i kube -A40

# 배포: 아래처럼 반드시 ~/kubespray 디렉토리에서 ansible-playbook 를 실행하자!
# Deploy Kubespray with Ansible Playbook - run the playbook as root
# The option `--become` is required, as for example writing SSL keys in /etc/,
# installing packages and interacting with various systemd daemons.
# ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml -e kube_version="1.33.3" --list-tasks # 배포 전, Task 목록 확인
ANSIBLE_FORCE_COLOR=true ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml -e kube_version="1.33.3" | tee kubespray_install.log

~~~(약 5분정도 소요)~~~
Saturday 31 January 2026  13:24:42 +0900 (0:10:01.288)       0:19:21.856 ******
===============================================================================
network_plugin/flannel : Flannel | Wait for flannel subnet.env file presence - 601.29s
download : Download_container | Download image if required ------------- 20.24s
download : Download_file | Download item ------------------------------- 17.08s
kubernetes/control-plane : Kubeadm | Initialize first control plane node (1st try) -- 16.16s
download : Download_container | Download image if required ------------- 13.97s
container-engine/containerd : Download_file | Download item ------------ 12.91s
download : Download_container | Download image if required ------------- 11.99s
download : Download_container | Download image if required ------------- 11.85s
container-engine/crictl : Download_file | Download item ---------------- 10.88s
download : Download_file | Download item ------------------------------- 10.32s
system_packages : Manage packages --------------------------------------- 9.99s
container-engine/runc : Download_file | Download item ------------------- 9.20s
container-engine/nerdctl : Download_file | Download item ---------------- 9.15s
download : Download_container | Download image if required -------------- 8.59s
etcdctl_etcdutl : Download_file | Download item ------------------------- 8.13s
download : Download_container | Download image if required -------------- 7.96s
etcd : Restart etcd ----------------------------------------------------- 7.73s
download : Download_container | Download image if required -------------- 7.65s
download : Download_file | Download item -------------------------------- 7.57s
container-engine/crictl : Extract_file | Unpacking archive -------------- 7.51s

# 설치 확인 : /root/.kube/config
more kubespray_install.log

kubectl get node -v=6
I0131 13:37:03.125332   32893 loader.go:402] Config loaded from file:  /root/.kube/config
I0131 13:37:03.126005   32893 envvar.go:172] "Feature gate default state" feature="InformerResourceVersion" enabled=false
I0131 13:37:03.126029   32893 envvar.go:172] "Feature gate default state" feature="InOrderInformers" enabled=true
I0131 13:37:03.126036   32893 envvar.go:172] "Feature gate default state" feature="WatchListClient" enabled=false
I0131 13:37:03.126042   32893 envvar.go:172] "Feature gate default state" feature="ClientsAllowCBOR" enabled=false
I0131 13:37:03.126052   32893 envvar.go:172] "Feature gate default state" feature="ClientsPreferCBOR" enabled=false
I0131 13:37:03.149892   32893 round_trippers.go:632] "Response" verb="GET" url="https://127.0.0.1:6443/api/v1/nodes?limit=500" status="200 OK" milliseconds=12
NAME      STATUS   ROLES           AGE   VERSION
k8s-ctr   Ready    control-plane   23m   v1.33.3

cat /root/.kube/config
kubectl config view

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://127.0.0.1:6443
  name: cluster.local
contexts:
- context:
    cluster: cluster.local
    user: kubernetes-admin
  name: kubernetes-admin@cluster.local
current-context: kubernetes-admin@cluster.local
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED


# k8s
kubectl get node -owide
NAME      STATUS   ROLES           AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                        KERNEL-VERSION                 CONTAINER-RUNTIME
k8s-ctr   Ready    control-plane   24m   v1.33.3   192.168.10.10   <none>        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.x86_64   containerd://2.1.5


kubectl get pod -A
NAMESPACE     NAME                              READY   STATUS             RESTARTS       AGE
kube-system   kube-apiserver-k8s-ctr            1/1     Running            0              24m
...(생략)...


# 기본 환경 정보 출력 저장
ip addr | tee -a ip_addr-2.txt 
ss -tnlp | tee -a ss-2.txt
df -hT | tee -a df-2.txt
findmnt | tee -a findmnt-2.txt
sysctl -a | tee -a sysctl-2.txt

# 파일 출력 비교 : 빠져나오기 ':q' -> ':q' => 변경된 부분이 어떤 동작과 역할인지 조사해보기! , ctrl + f / b
vi -d ip_addr-1.txt ip_addr-2.txt

vi -d ss-1.txt ss-2.txt

vi -d df-1.txt df-2.txt

vi -d findmnt-1.txt findmnt-2.txt

vi -d sysctl-1.txt sysctl-2.txt

 

위 결과를 보듯이 kubespray 를 이용하면 클러스터 구서이 간편하며 이때 변경하고자 하는 부분의 변수만 설정하면 됩니다.

주로 사용하는 변수 목록은 아래 링크를 참고하세요

Ansible Playbook & Role 분석

루트에 있는 cluster.yml --> playbooks/cluster.yml 에 있는 TASK 구조는

  • "사전 검증 → 접근/부트스트랩 → etcd → node → control-plane → CNI → 애드온" 절차를 거칩니다.

PLAY 과정을 좀더 상세히 살펴보겠습니다.

cat kubespray_install.log | grep -E 'PLAY'
PLAY [Check Ansible version] ******************************    # Kubespray가 지원하는 Ansible 버전인지 확인
PLAY [Inventory setup and validation] *********************    # inventory 설정 정합성 검증 : kube_control_plane, etcd 그룹 존재 여부, etcd 노드 수 (odd 권장), Pod CIDR / Service CIDR 유효성, Kubernetes 버전 지원 여부
PLAY [Install bastion ssh config] *************************    # Bastion(점프 호스트) 환경 지원 : bastion 미사용 시 대부분 skip
PLAY [Bootstrap hosts for Ansible] ************************    # 모든 노드를 Ansible 실행 가능한 상태로 만듦 : Python 설치, sudo 권한 확보, 기본 패키지 설치, /usr/bin/python 보장
PLAY [Gather facts] ***************************************    # Ansible fact 수집 : 이후 TASK들이: when: ansible_os_family == "Debian" 같은 조건 분기에서 사용됨
PLAY [Prepare for etcd install] ***************************    # etcd 설치 전 사전 준비 : etcd user 생성, 디렉터리 생성, 방화벽 / 포트, cert 경로 준비
PLAY [Add worker nodes to the etcd play if needed] ********    # worker + etcd 겸용 노드 지원 : kube_node: + etcd: 둘 다 포함된 노드를 etcd PLAY에 추가
PLAY [Install etcd] ***************************************    # etcd 설치 : etcd binary 설치, TLS 인증서 생성, systemd 등록, 클러스터 구성
PLAY [Install Kubernetes nodes] ***************************    # 모든 노드에 공통 K8s 컴포넌트 설치, 아직 클러스터 join은 안 함
PLAY [Install the control plane] **************************    # kube_control_plane 그룹 : control-plane 노드 구성
PLAY [Invoke kubeadm and install a CNI] *******************    # kubeadm init / join 실행 , 네트워크(CNI) 설치
PLAY [Install Calico Route Reflector] *********************    # Calico BGP 미사용환경이면 skip
PLAY [Patch Kubernetes for Windows] ***********************    # Linux-only 환경이면 skip
PLAY [Install Kubernetes apps] ****************************    # 기본 애드온 설치 : CoreDNS, metrics-server 등
PLAY [Apply resolv.conf changes now that cluster DNS is up]    # CoreDNS 설치 후 노드의 DNS 설정 최종 정리 : bootstrap 단계에선 임시 resolv.conf 사용, 클러스터 DNS 안정화 후 되돌림
PLAY RECAP ************************************************

# TASK 는 총 559개입니다.
cat kubespray_install.log | grep -E 'TASK' | wc -l
559

cat kubespray_install.log | grep -E 'TASK'
TASK [Check 2.17.3 <= Ansible version < 2.18.0] ********************************
TASK [Check that python netaddr is installed] **********************************
TASK [Check that jinja is not too old (install via pip)] ***********************
TASK [dynamic_groups : Match needed groups by their old names or definition] ***
TASK [validate_inventory : Stop if removed tags are used] **********************
TASK [validate_inventory : Stop if kube_control_plane group is empty] **********
TASK [validate_inventory : Stop if etcd group is empty in external etcd mode] ***
TASK [validate_inventory : Stop if unsupported version of Kubernetes] **********
TASK [validate_inventory : Stop if known booleans are set as strings (Use JSON format on CLI: -e "{'key': true }")] ***
TASK [validate_inventory : Stop if even number of etcd hosts] ******************
TASK [validate_inventory : Guarantee that enough network address space is available for all pods] ***
TASK [validate_inventory : Check that kube_service_addresses is a network range] ***
TASK [validate_inventory : Check that kube_pods_subnet is a network range] *****
TASK [validate_inventory : Check that kube_pods_subnet does not collide with kube_service_addresses] ***
TASK [validate_inventory : Check that ipv4 IP range is enough for the nodes] ***
TASK [validate_inventory : Stop if unsupported options selected] ***************
TASK [validate_inventory : Ensure minimum containerd version] ******************
TASK [bootstrap_os : Fetch /etc/os-release] ************************************
TASK [bootstrap_os : Include tasks] ********************************************
TASK [bootstrap_os : Gather host facts to get ansible_distribution_version ansible_distribution_major_version] ***
TASK [bootstrap_os : Add proxy to yum.conf or dnf.conf if http_proxy is defined] ***
TASK [bootstrap_os : Check presence of fastestmirror.conf] *********************
TASK [system_packages : Gather OS information] *********************************
TASK [system_packages : Remove legacy docker repo file] ************************
TASK [system_packages : Manage packages] ***************************************
TASK [bootstrap_os : Create remote_tmp for it is used by another module] *******
TASK [bootstrap_os : Gather facts] *********************************************
TASK [bootstrap_os : Assign inventory name to unconfigured hostnames (non-CoreOS, non-Flatcar, Suse and ClearLinux, non-Fedora)] ***
TASK [bootstrap_os : Ensure bash_completion.d folder exists] *******************
TASK [network_facts : Gather ansible_default_ipv4] *****************************
TASK [network_facts : Set fallback_ip] *****************************************
TASK [network_facts : Gather ansible_default_ipv6] *****************************
TASK [network_facts : Set fallback_ip6] ****************************************
TASK [network_facts : Set main access ip(access_ip based on ipv4_stack/ipv6_stack options).] ***
TASK [network_facts : Set main ip(ip based on ipv4_stack/ipv6_stack options).] ***
TASK [network_facts : Set main access ips(mixed ips for dualstack).] ***********
TASK [network_facts : Set main ips(mixed ips for dualstack).] ******************
TASK [Gather minimal facts] ****************************************************
TASK [Gather necessary facts (network)] ****************************************
TASK [Gather necessary facts (hardware)] ***************************************
TASK [adduser : User | Create User Group] **************************************
TASK [adduser : User | Create User] ********************************************
TASK [kubernetes/preinstall : Check if /etc/fstab exists] **********************
TASK [kubernetes/preinstall : Remove swapfile from /etc/fstab] *****************
TASK [kubernetes/preinstall : Mask swap.target (persist swapoff)] **************
TASK [kubernetes/preinstall : Disable swap] ************************************
TASK [kubernetes/preinstall : Check resolvconf] ********************************
TASK [kubernetes/preinstall : Check existence of /etc/resolvconf/resolv.conf.d] ***
TASK [kubernetes/preinstall : Check status of /etc/resolv.conf] ****************
TASK [kubernetes/preinstall : Fetch resolv.conf] *******************************
TASK [kubernetes/preinstall : NetworkManager | Check if host has NetworkManager] ***
TASK [kubernetes/preinstall : Check systemd-resolved] **************************
TASK [kubernetes/preinstall : Set default dns if remove_default_searchdomains is false] ***
TASK [kubernetes/preinstall : Set dns facts] ***********************************
TASK [kubernetes/preinstall : Check if kubelet is configured] ******************
TASK [kubernetes/preinstall : Check if early DNS configuration stage] **********
TASK [kubernetes/preinstall : Target resolv.conf files] ************************
TASK [kubernetes/preinstall : Check if /etc/dhclient.conf exists] **************
TASK [kubernetes/preinstall : Check if /etc/dhcp/dhclient.conf exists] *********
TASK [kubernetes/preinstall : Target dhclient hook file for Red Hat family] ****
TASK [kubernetes/preinstall : Check /usr readonly] *****************************
TASK [kubernetes/preinstall : Stop if non systemd OS type] *********************
TASK [kubernetes/preinstall : Stop if the os does not support] *****************
TASK [kubernetes/preinstall : Stop if memory is too small for control plane nodes] ***
TASK [kubernetes/preinstall : Stop if memory is too small for nodes] ***********
TASK [kubernetes/preinstall : Stop if cgroups are not enabled on nodes] ********
TASK [kubernetes/preinstall : Stop if ip var does not match local ips] *********
TASK [kubernetes/preinstall : Stop if access_ip is not pingable] ***************
TASK [kubernetes/preinstall : Stop if bad hostname] ****************************
TASK [kubernetes/preinstall : Stop if /etc/resolv.conf has no configured nameservers] ***
TASK [kubernetes/preinstall : Create kubernetes directories] *******************
TASK [kubernetes/preinstall : Create other directories of root owner] **********
TASK [kubernetes/preinstall : Check if kubernetes kubeadm compat cert dir exists] ***
TASK [kubernetes/preinstall : Create kubernetes kubeadm compat cert dir (kubernetes/kubeadm issue 1498)] ***
TASK [kubernetes/preinstall : Create cni directories] **************************
TASK [kubernetes/preinstall : NetworkManager | Ensure NetworkManager conf.d dir] ***
TASK [kubernetes/preinstall : NetworkManager | Prevent NetworkManager from managing K8S interfaces (kube-ipvs0/nodelocaldns)] ***
TASK [kubernetes/preinstall : NetworkManager | Add nameservers to NM configuration] ***
TASK [kubernetes/preinstall : Set default dns if remove_default_searchdomains is false] ***
TASK [kubernetes/preinstall : NetworkManager | Add DNS search to NM configuration] ***
TASK [kubernetes/preinstall : NetworkManager | Add DNS options to NM configuration] ***
TASK [kubernetes/preinstall : Confirm selinux deployed] ************************
TASK [kubernetes/preinstall : Set selinux policy] ******************************
TASK [kubernetes/preinstall : Clean previously used sysctl file locations] *****
TASK [kubernetes/preinstall : Stat sysctl file configuration] ******************
TASK [kubernetes/preinstall : Change sysctl file path to link source if linked] ***
TASK [kubernetes/preinstall : Make sure sysctl file path folder exists] ********
TASK [kubernetes/preinstall : Enable ip forwarding] ****************************
TASK [kubernetes/preinstall : Check if we need to set fs.may_detach_mounts] ****
TASK [kubernetes/preinstall : Ensure kubelet expected parameters are set] ******
TASK [kubernetes/preinstall : Disable fapolicyd service] ***********************
TASK [kubernetes/preinstall : Check if we are running inside a Azure VM] *******
TASK [container-engine/validate-container-engine : Validate-container-engine | check if fedora coreos] ***
TASK [container-engine/validate-container-engine : Validate-container-engine | set is_ostree] ***
TASK [container-engine/validate-container-engine : Ensure kubelet systemd unit exists] ***
TASK [container-engine/validate-container-engine : Populate service facts] *****
TASK [container-engine/validate-container-engine : Check if containerd is installed] ***
TASK [container-engine/validate-container-engine : Check if docker is installed] ***
TASK [container-engine/validate-container-engine : Check if crio is installed] ***
TASK [container-engine/containerd-common : Containerd-common | check if fedora coreos] ***
TASK [container-engine/containerd-common : Containerd-common | set is_ostree] ***
TASK [container-engine/runc : Runc | check if fedora coreos] *******************
TASK [container-engine/runc : Runc | set is_ostree] ****************************
TASK [container-engine/runc : Runc | Uninstall runc package managed by package manager] ***
TASK [container-engine/runc : Runc | Download runc binary] *********************
TASK [container-engine/runc : Prep_download | Set a few facts] *****************
TASK [container-engine/runc : Download_file | Set pathname of cached file] *****
TASK [container-engine/runc : Download_file | Create dest directory on node] ***
TASK [container-engine/runc : Download_file | Download item] *******************
TASK [container-engine/runc : Download_file | Extract file archives] ***********
TASK [container-engine/runc : Copy runc binary from download dir] **************
TASK [container-engine/runc : Runc | Remove orphaned binary] *******************
TASK [container-engine/crictl : Install crictl] ********************************
TASK [container-engine/crictl : Crictl | Download crictl] **********************
TASK [container-engine/crictl : Prep_download | Set a few facts] ***************
TASK [container-engine/crictl : Download_file | Set pathname of cached file] ***
TASK [container-engine/crictl : Download_file | Create dest directory on node] ***
TASK [container-engine/crictl : Download_file | Download item] *****************
TASK [container-engine/crictl : Download_file | Extract file archives] *********
TASK [container-engine/crictl : Extract_file | Unpacking archive] **************
TASK [container-engine/crictl : Install crictl config] *************************
TASK [container-engine/crictl : Copy crictl binary from download dir] **********
TASK [container-engine/nerdctl : Nerdctl | Download nerdctl] *******************
TASK [container-engine/nerdctl : Prep_download | Set a few facts] **************
TASK [container-engine/nerdctl : Download_file | Set pathname of cached file] ***
TASK [container-engine/nerdctl : Download_file | Create dest directory on node] ***
TASK [container-engine/nerdctl : Download_file | Download item] ****************
TASK [container-engine/nerdctl : Download_file | Extract file archives] ********
TASK [container-engine/nerdctl : Extract_file | Unpacking archive] *************
TASK [container-engine/nerdctl : Nerdctl | Copy nerdctl binary from download dir] ***
TASK [container-engine/nerdctl : Nerdctl | Create configuration dir] ***********
TASK [container-engine/nerdctl : Nerdctl | Install nerdctl configuration] ******
TASK [container-engine/containerd : Containerd | Download containerd] **********
TASK [container-engine/containerd : Prep_download | Set a few facts] ***********
TASK [container-engine/containerd : Download_file | Set pathname of cached file] ***
TASK [container-engine/containerd : Download_file | Create dest directory on node] ***
TASK [container-engine/containerd : Download_file | Download item] *************
TASK [container-engine/containerd : Download_file | Extract file archives] *****
TASK [container-engine/containerd : Containerd | Unpack containerd archive] ****
TASK [container-engine/containerd : Containerd | Generate systemd service for containerd] ***
TASK [container-engine/containerd : Containerd | Ensure containerd directories exist] ***
TASK [container-engine/containerd : Containerd | Generate default base_runtime_spec] ***
TASK [container-engine/containerd : Containerd | Store generated default base_runtime_spec] ***
TASK [container-engine/containerd : Containerd | Write base_runtime_specs] *****
TASK [container-engine/containerd : Containerd | Copy containerd config file] ***
TASK [container-engine/containerd : Containerd | Create registry directories] ***
TASK [container-engine/containerd : Containerd | Write hosts.toml file] ********
TASK [container-engine/containerd : Containerd | Ensure containerd is started and enabled] ***
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Prep_download | Register docker images info] ******************
TASK [download : Prep_download | Create staging directory on remote node] ******
TASK [download : Download | Get kubeadm binary and list of required images] ****
TASK [download : Prep_kubeadm_images | Download kubeadm binary] ****************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Prep_kubeadm_images | Copy kubeadm binary from download dir to system path] ***
TASK [download : Prep_kubeadm_images | Create kubeadm config] ******************
TASK [download : Prep_kubeadm_images | Generate list of required images] *******
TASK [download : Prep_kubeadm_images | Parse list of images] *******************
TASK [download : Prep_kubeadm_images | Convert list of images to dict for later use] ***
TASK [download : Download | Download files / images] ***************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Extract_file | Unpacking archive] *****************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Extract_file | Unpacking archive] *****************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Extract_file | Unpacking archive] *****************************
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Prep_download | Set a few facts] ******************************
TASK [download : Download_file | Set pathname of cached file] ******************
TASK [download : Download_file | Create dest directory on node] ****************
TASK [download : Download_file | Download item] ********************************
TASK [download : Download_file | Extract file archives] ************************
TASK [download : Extract_file | Unpacking archive] *****************************
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [download : Set default values for flag variables] ************************
TASK [download : Set_container_facts | Display the name of the image being processed] ***
TASK [download : Set_container_facts | Set if containers should be pulled by digest] ***
TASK [download : Set_container_facts | Define by what name to pull the image] ***
TASK [download : Set_container_facts | Define file name of image] **************
TASK [download : Set_container_facts | Define path of image] *******************
TASK [download : Set image save/load command for containerd] *******************
TASK [download : Set image save/load command for containerd on localhost] ******
TASK [download : Download_container | Prepare container download] **************
TASK [download : Check_pull_required |  Generate a list of information about the images on a node] ***
TASK [download : Check_pull_required | Set pull_required if the desired image is not yet loaded] ***
TASK [download : debug] ********************************************************
TASK [download : Download_container | Download image if required] **************
TASK [download : Download_container | Remove container image from cache] *******
TASK [Gathering Facts] *********************************************************
TASK [Check if nodes needs etcd client certs (depends on network_plugin)] ******
TASK [adduser : User | Create User Group] **************************************
TASK [adduser : User | Create User] ********************************************
TASK [adduser : User | Create User Group] **************************************
TASK [adduser : User | Create User] ********************************************
TASK [etcd : Check etcd certs] *************************************************
TASK [etcd : Check_certs | Register certs that have already been generated on first etcd node] ***
TASK [etcd : Check_certs | Set default value for 'sync_certs', 'gen_certs' and 'etcd_secret_changed' to false] ***
TASK [etcd : Check certs | Register ca and etcd admin/member certs on etcd hosts] ***
TASK [etcd : Check certs | Register ca and etcd node certs on kubernetes hosts] ***
TASK [etcd : Check_certs | Set 'gen_certs' to true if expected certificates are not on the first etcd node(1/2)] ***
TASK [etcd : Check_certs | Set 'gen_certs' to true if expected certificates are not on the first etcd node(2/2)] ***
TASK [etcd : Check_certs | Set 'gen_*_certs' groups to track which nodes needs to have certs generated on first etcd node] ***
TASK [etcd : Check_certs | Set 'etcd_member_requires_sync' to true if ca or member/admin cert and key don't exist on etcd member or checksum doesn't match] ***
TASK [etcd : Check_certs | Set 'sync_certs' to true] ***************************
TASK [etcd : Generate etcd certs] **********************************************
TASK [etcd : Gen_certs | create etcd cert dir] *********************************
TASK [etcd : Gen_certs | create etcd script dir (on k8s-ctr1)] *****************
TASK [etcd : Gen_certs | write openssl config] *********************************
TASK [etcd : Gen_certs | copy certs generation script] *************************
TASK [etcd : Gen_certs | run cert generation script for etcd and kube control plane nodes] ***
TASK [etcd : Gen_certs | run cert generation script for all clients] ***********
TASK [etcd : Gen_certs | check certificate permissions] ************************
TASK [etcd : Trust etcd CA] ****************************************************
TASK [etcd : Gen_certs | target ca-certificate store file] *********************
TASK [etcd : Gen_certs | add CA to trusted CA dir] *****************************
TASK [etcd : Gen_certs | update ca-certificates (RedHat)] **********************
TASK [etcd : Trust etcd CA on nodes if needed] *********************************
TASK [etcd : Gen_certs | target ca-certificate store file] *********************
TASK [etcd : Gen_certs | add CA to trusted CA dir] *****************************
TASK [etcd : Gen_certs | Get etcd certificate serials] *************************
TASK [etcd : Set etcd_client_cert_serial] **************************************
TASK [etcdctl_etcdutl : Download etcd binary] **********************************
TASK [etcdctl_etcdutl : Prep_download | Set a few facts] ***********************
TASK [etcdctl_etcdutl : Download_file | Set pathname of cached file] ***********
TASK [etcdctl_etcdutl : Download_file | Create dest directory on node] *********
TASK [etcdctl_etcdutl : Download_file | Download item] *************************
TASK [etcdctl_etcdutl : Download_file | Extract file archives] *****************
TASK [etcdctl_etcdutl : Extract_file | Unpacking archive] **********************
TASK [etcdctl_etcdutl : Copy etcd binary] **************************************
TASK [etcdctl_etcdutl : Copy etcdctl and etcdutl binary from download dir] *****
TASK [etcdctl_etcdutl : Create etcdctl wrapper script] *************************
TASK [etcd : Install etcd] *****************************************************
TASK [etcd : Get currently-deployed etcd version] ******************************
TASK [etcd : Restart etcd if necessary] ****************************************
TASK [etcd : Install | Copy etcd binary from download dir] *********************
TASK [etcd : Configure etcd] ***************************************************
TASK [etcd : Configure | Check if etcd cluster is healthy] *********************
TASK [etcd : Configure | Refresh etcd config] **********************************
TASK [etcd : Refresh config | Create etcd config file] *************************
TASK [etcd : Configure | Copy etcd.service systemd file] ***********************
TASK [etcd : Configure | reload systemd] ***************************************
TASK [etcd : Configure | Ensure etcd is running] *******************************
TASK [etcd : Configure | Wait for etcd cluster to be healthy] ******************
TASK [etcd : Configure | Check if member is in etcd cluster] *******************
TASK [etcd : Refresh etcd config] **********************************************
TASK [etcd : Refresh config | Create etcd config file] *************************
TASK [etcd : Refresh etcd config again for idempotency] ************************
TASK [etcd : Refresh config | Create etcd config file] *************************
TASK [kubernetes/node : Set kubelet_cgroup_driver_detected fact for containerd] ***
TASK [kubernetes/node : Set kubelet_cgroup_driver] *****************************
TASK [kubernetes/node : Ensure /var/lib/cni exists] ****************************
TASK [kubernetes/node : Install | Copy kubelet binary from download dir] *******
TASK [kubernetes/node : Ensure nodePort range is reserved] *********************
TASK [kubernetes/node : Verify if br_netfilter module exists] ******************
TASK [kubernetes/node : Verify br_netfilter module path exists] ****************
TASK [kubernetes/node : Enable br_netfilter module] ****************************
TASK [kubernetes/node : Persist br_netfilter module] ***************************
TASK [kubernetes/node : Check if bridge-nf-call-iptables key exists] ***********
TASK [kubernetes/node : Enable bridge-nf-call tables] **************************
TASK [kubernetes/node : Set kubelet api version to v1beta1] ********************
TASK [kubernetes/node : Write kubelet environment config file (kubeadm)] *******
TASK [kubernetes/node : Write kubelet config file] *****************************
TASK [kubernetes/node : Write kubelet systemd init file] ***********************
TASK [kubernetes/node : Enable kubelet] ****************************************
TASK [kubernetes/control-plane : Pre-upgrade | Delete control plane manifests if etcd secrets changed] ***
TASK [kubernetes/control-plane : Create kube-scheduler config] *****************
TASK [kubernetes/control-plane : Install | Copy kubectl binary from download dir] ***
TASK [kubernetes/control-plane : Install kubectl bash completion] **************
TASK [kubernetes/control-plane : Set kubectl bash completion file permissions] ***
TASK [kubernetes/control-plane : Check which kube-control nodes are already members of the cluster] ***
TASK [kubernetes/control-plane : Set fact first_kube_control_plane] ************
TASK [kubernetes/control-plane : Kubeadm | Check if kubeadm has already run] ***
TASK [kubernetes/control-plane : Kubeadm | aggregate all SANs] *****************
TASK [kubernetes/control-plane : Kubeadm | Create kubeadm config] **************
TASK [kubernetes/control-plane : Kubeadm | Initialize first control plane node (1st try)] ***
TASK [kubernetes/control-plane : Create kubeadm token for joining nodes with 24h expiration (default)] ***
TASK [kubernetes/control-plane : Set kubeadm_token] ****************************
TASK [kubernetes/control-plane : Kubeadm | Join other control plane nodes] *****
TASK [kubernetes/control-plane : Set kubeadm_discovery_address] ****************
TASK [kubernetes/control-plane : Upload certificates so they are fresh and not expired] ***
TASK [kubernetes/control-plane : Parse certificate key if not set] *************
TASK [kubernetes/control-plane : Wait for k8s apiserver] ***********************
TASK [kubernetes/control-plane : Check already run] ****************************
TASK [kubernetes/control-plane : Kubeadm | Remove taint for control plane node with node role] ***
TASK [kubernetes/control-plane : Include kubeadm secondary server apiserver fixes] ***
TASK [kubernetes/control-plane : Update server field in component kubeconfigs] ***
TASK [kubernetes/control-plane : Include kubelet client cert rotation fixes] ***
TASK [kubernetes/control-plane : Fixup kubelet client cert rotation 1/2] *******
TASK [kubernetes/control-plane : Fixup kubelet client cert rotation 2/2] *******
TASK [kubernetes/control-plane : Install script to renew K8S control plane certificates] ***
TASK [kubernetes/control-plane : Renew K8S control plane certificates monthly 1/2] ***
TASK [kubernetes/control-plane : Renew K8S control plane certificates monthly 2/2] ***
TASK [kubernetes/client : Set external kube-apiserver endpoint] ****************
TASK [kubernetes/client : Create kube config dir for current/ansible become user] ***
TASK [kubernetes/client : Copy admin kubeconfig to current/ansible become user home] ***
TASK [kubernetes/client : Wait for k8s apiserver] ******************************
TASK [kubernetes-apps/cluster_roles : Kubernetes Apps | Wait for kube-apiserver] ***
TASK [kubernetes-apps/cluster_roles : Kubernetes Apps | Add ClusterRoleBinding to admit nodes] ***
TASK [kubernetes-apps/cluster_roles : Apply workaround to allow all nodes with cert O=system:nodes to register] ***
TASK [kubernetes-apps/cluster_roles : Kubernetes Apps | Remove old webhook ClusterRole] ***
TASK [kubernetes-apps/cluster_roles : Kubernetes Apps | Remove old webhook ClusterRoleBinding] ***
TASK [kubernetes-apps/cluster_roles : PriorityClass | Copy k8s-cluster-critical-pc.yml file] ***
TASK [kubernetes-apps/cluster_roles : PriorityClass | Create k8s-cluster-critical] ***
TASK [kubernetes/kubeadm : Set kubeadm_discovery_address] **********************
TASK [kubernetes/kubeadm : Check if kubelet.conf exists] ***********************
TASK [kubernetes/kubeadm : Check if kubeadm CA cert is accessible] *************
TASK [kubernetes/kubeadm : Fetch CA certificate from control plane node] *******
TASK [kubernetes/kubeadm : Check if discovery kubeconfig exists] ***************
TASK [kubernetes/kubeadm : Get current resourceVersion of kube-proxy configmap] ***
TASK [kubernetes/kubeadm : Update server field in kube-proxy kubeconfig] *******
TASK [kubernetes/kubeadm : Get new resourceVersion of kube-proxy configmap] ****
TASK [kubernetes/kubeadm : Set ca.crt file permission] *************************
TASK [kubernetes/kubeadm : Restart all kube-proxy pods to ensure that they load the new configmap] ***
TASK [kubernetes/node-label : Kubernetes Apps | Wait for kube-apiserver] *******
TASK [kubernetes/node-label : Set role node label to empty list] ***************
TASK [kubernetes/node-label : Set inventory node label to empty list] **********
TASK [kubernetes/node-label : debug] *******************************************
TASK [kubernetes/node-label : debug] *******************************************
TASK [kubernetes/node-taint : Set role and inventory node taint to empty list] ***
TASK [kubernetes/node-taint : debug] *******************************************
TASK [kubernetes/node-taint : debug] *******************************************
TASK [network_plugin/cni : CNI | make sure /opt/cni/bin exists] ****************
TASK [network_plugin/cni : CNI | Copy cni plugins] *****************************
TASK [network_plugin/cni : CNI | make sure /opt/cni/bin exists] ****************
TASK [network_plugin/cni : CNI | Copy cni plugins] *****************************
TASK [network_plugin/flannel : Flannel | Create Flannel manifests] *************
TASK [network_plugin/flannel : Flannel | Start Resources] **********************
TASK [network_plugin/flannel : Flannel | Wait for flannel subnet.env file presence] ***
TASK [win_nodes/kubernetes_patch : Ensure that user manifests directory exists] ***
TASK [win_nodes/kubernetes_patch : Check current nodeselector for kube-proxy daemonset] ***
TASK [win_nodes/kubernetes_patch : Apply nodeselector patch for kube-proxy daemonset] ***
TASK [win_nodes/kubernetes_patch : debug] **************************************
TASK [win_nodes/kubernetes_patch : debug] **************************************
TASK [kubernetes-apps/ansible : Kubernetes Apps | Wait for kube-apiserver] *****
TASK [kubernetes-apps/ansible : Kubernetes Apps | CoreDNS] *********************
TASK [kubernetes-apps/helm : Helm | Gather os specific variables] **************
TASK [kubernetes-apps/helm : Helm | Install PyYaml] ****************************
TASK [kubernetes-apps/helm : Helm | Download helm] *****************************
TASK [kubernetes-apps/helm : Prep_download | Set a few facts] ******************
TASK [kubernetes-apps/helm : Download_file | Set pathname of cached file] ******
TASK [kubernetes-apps/helm : Download_file | Create dest directory on node] ****
TASK [kubernetes-apps/helm : Download_file | Download item] ********************
TASK [kubernetes-apps/helm : Download_file | Extract file archives] ************
TASK [kubernetes-apps/helm : Extract_file | Unpacking archive] *****************
TASK [kubernetes-apps/helm : Helm | Copy helm binary from download dir] ********
TASK [kubernetes-apps/helm : Helm | Get helm completion] ***********************
TASK [kubernetes-apps/helm : Helm | Install helm completion] *******************
TASK [kubernetes-apps/metrics_server : Metrics Server | Delete addon dir] ******
TASK [kubernetes-apps/metrics_server : Metrics Server | Create addon dir] ******
TASK [kubernetes-apps/metrics_server : Metrics Server | Templates list] ********
TASK [kubernetes-apps/metrics_server : Metrics Server | Create manifests] ******
TASK [kubernetes-apps/metrics_server : Metrics Server | Apply manifests] *******
TASK [adduser : User | Create User Group] **************************************
TASK [adduser : User | Create User] ********************************************
TASK [kubernetes/preinstall : Check resolvconf] ********************************
TASK [kubernetes/preinstall : Check existence of /etc/resolvconf/resolv.conf.d] ***
TASK [kubernetes/preinstall : Check status of /etc/resolv.conf] ****************
TASK [kubernetes/preinstall : Fetch resolv.conf] *******************************
TASK [kubernetes/preinstall : NetworkManager | Check if host has NetworkManager] ***
TASK [kubernetes/preinstall : Check systemd-resolved] **************************
TASK [kubernetes/preinstall : Set default dns if remove_default_searchdomains is false] ***
TASK [kubernetes/preinstall : Set dns facts] ***********************************
TASK [kubernetes/preinstall : Check if kubelet is configured] ******************
TASK [kubernetes/preinstall : Check if early DNS configuration stage] **********
TASK [kubernetes/preinstall : Target resolv.conf files] ************************
TASK [kubernetes/preinstall : Check if /etc/dhclient.conf exists] **************
TASK [kubernetes/preinstall : Check if /etc/dhcp/dhclient.conf exists] *********
TASK [kubernetes/preinstall : Target dhclient hook file for Red Hat family] ****
TASK [kubernetes/preinstall : Check /usr readonly] *****************************
TASK [kubernetes/preinstall : NetworkManager | Ensure NetworkManager conf.d dir] ***
TASK [kubernetes/preinstall : NetworkManager | Prevent NetworkManager from managing K8S interfaces (kube-ipvs0/nodelocaldns)] ***
TASK [kubernetes/preinstall : NetworkManager | Add nameservers to NM configuration] ***
TASK [kubernetes/preinstall : Set default dns if remove_default_searchdomains is false] ***
TASK [kubernetes/preinstall : NetworkManager | Add DNS search to NM configuration] ***
TASK [kubernetes/preinstall : NetworkManager | Add DNS options to NM configuration] ***

 

playbooks/cluster.yml 설치 절차

  • cluster.yml은 Kubespray의 메인 플레이북으로, 클러스터 생성의 전체 과정을 정의합니다.

https://velog.io/@bytebliss/kubespray-Ansible-Playbook-Role-%EB%B6%84%EC%84%9D
https://sirzzang.github.io/kubernetes/Kubernetes-Kubespray-04-01-00/

playbooks/cluster.yml : 전체 과정

A. 초기화 및 정보 수집 (Boilerplate & Facts)

  • 모든 노드에 공통적으로 필요한 설정을 적용하고, 각 서버의 사양 정보를 수집하여 이후 설치 단계에서 변수로 활용합니다.

B. 인프라 및 엔진 준비 (Prepare for etcd & container-engine)

  • kubernetes/preinstall: 방화벽, 커널 파라미터, Swap 비활성화 등 K8s 설치를 위한 OS 최적화를 수행합니다.
  • container-engine: Docker나 Containerd 같은 컨테이너 런타임을 설치합니다.
  • download: 설치에 필요한 모든 바이너리와 이미지들을 미리 다운로드합니다.

C. 데이터 저장소 및 노드 구성 (Etcd & K8s Nodes)

  • etcd: 쿠버네티스의 상태 정보를 저장하는 DB 클러스터를 구축합니다.
  • kubernetes/node: 모든 노드에 Kubelet, Kube-proxy 등 기초 컴포넌트를 설치합니다.

D. 컨트롤 플레인 및 네트워크 (Control Plane & CNI)

  • control-plane: 마스터 노드에 API 서버, 스케줄러 등을 설정합니다.
  • kubeadm: kubeadm init 또는 join을 통해 클러스터를 하나로 묶습니다.
  • network_plugin: Calico, Flannel 등 CNI를 설치하여 파드 간 통신을 가능하게 합니다.

E. 부가 서비스 설치 (Apps & DNS)

  • Ingress Controller, Storage Provisioner 등 클러스터 운영에 필요한 앱들을 배포하고, 최종적으로 노드의 DNS(resolv.conf)가 클러스터 내부 DNS를 바라보도록 수정합니다.

파일 내용을 좀 더 상세히 분석해보겠습니다.

cat playbooks/cluster.yml
---
- name: Common tasks for every playbooks
  import_playbook: boilerplate.yml

- name: Gather facts
  import_playbook: internal_facts.yml

- name: Prepare for etcd install
  hosts: k8s_cluster:etcd        # 이 작업은 쿠버네티스 클러스터에 속한 모든 노드(k8s_cluster)와 etcd 전용 노드(etcd) 전체를 대상으로 실행.
  gather_facts: false            
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}" # 하나라도 에러가 발생하면 전체 배포 프로세스를 즉시 중단합니다.
  environment: "{{ proxy_disable_env }}"                     # 인터넷 통신 시 프록시 설정을 제어합니다. 주로 내부 통신 시 프록시를 우회하도록 설정할 때 사용됩니다.
  roles:
    - { role: kubespray_defaults }                           # Kubespray 전체에서 사용하는 공통 변수(경로, 버전 등)를 로드합니다.
    - { role: kubernetes/preinstall, tags: preinstall }      # OS 레벨의 사전 설정을 수행합니다. 예) 커널 파라미터(sysctl) 최적화 등
    - { role: "container-engine", tags: "container-engine", when: deploy_container_engine } # 컨테이너 런타임(containerd, Docker 등)을 설치합니다.
    - { role: download, tags: download, when: "not skip_downloads" } # 설치에 필요한 모든 바이너리와 컨테이너 이미지를 다운로드합니다.
 
- name: Install etcd
  vars:
    etcd_cluster_setup: true  # etcd를 “클러스터 모드”로 초기화. 단일 노드라도 내부 로직은 클러스터 기준으로 동작. 신규 클러스터 생성 시 반드시 true
    etcd_events_cluster_setup: "{{ etcd_events_cluster_enabled }}" # true 일 경우 events 전용 etcd 클러스터를 별도로 구성, 대부분 환경에서는 false
  import_playbook: install_etcd.yml  # Playbook을 import 하는 구조, 즉, 여기에는 hosts:가 없고 install_etcd.yml 안에 실제 Play들이 있음.

- name: Install Kubernetes nodes    # 이 노드를 "kubelet이 돌아가는 Kubernetes 노드"로 만든다.
  hosts: k8s_cluster                # 모든 k8s 노드 : control-plane , worker
  gather_facts: false
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
  environment: "{{ proxy_disable_env }}"
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/node, tags: node }  # 예) kubelet 설치 등

- name: Install the control plane
  hosts: kube_control_plane         # control-plane 노드만
  gather_facts: false
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
  environment: "{{ proxy_disable_env }}"
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/control-plane, tags: control-plane } # 설치 : apiserver, kcm, scheduler
    - { role: kubernetes/client, tags: client }               # kubectl 설치, admin kubeconfig 생성 준비
    - { role: kubernetes-apps/cluster_roles, tags: cluster-roles } # Kubernetes 기본 ClusterRole 생성, bootstrap / node / admin 권한 관련

- name: Invoke kubeadm and install a CNI                      
  hosts: k8s_cluster
  gather_facts: false
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
  environment: "{{ proxy_disable_env }}"
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/kubeadm, tags: kubeadm}           # kubeadm init* / join* 실행
    - { role: kubernetes/node-label, tags: node-label }    # inventory 기반으로 node label 설정
    - { role: kubernetes/node-taint, tags: node-taint }    # control-plane taint 설정
    - { role: kubernetes-apps/common_crds }                # CNI / ingress / storage에서 사용하는 공통 CRD 먼저 설치
    - { role: network_plugin, tags: network }              # 실제 CNI 플러그인 설치 단계* : Calico / Cilium / Flannel / Weave 등

- name: Install Calico Route Reflector
  hosts: calico_rr
  gather_facts: false
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
  environment: "{{ proxy_disable_env }}"
  roles:
    - { role: kubespray_defaults }
    - { role: network_plugin/calico/rr, tags: ['network', 'calico_rr'] }

- name: Patch Kubernetes for Windows
  hosts: kube_control_plane[0]
  gather_facts: false
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
  environment: "{{ proxy_disable_env }}"
  roles:
    - { role: kubespray_defaults }
    - { role: win_nodes/kubernetes_patch, tags: ["control-plane", "win_nodes"] }

- name: Install Kubernetes apps
  hosts: kube_control_plane        # control-plane 노드만 : 실제로는 클러스터 전체에 리소스를 배포하지만, 명령 실행 위치만 control-plane
  gather_facts: false
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
  environment: "{{ proxy_disable_env }}"
  roles:
    - { role: kubespray_defaults } # 공통 변수 로딩, apps 관련 enable/disable 값 정리
    - { role: kubernetes-apps/external_cloud_controller, tags: external-cloud-controller } # cloud-controller-manager 외부 실행 버전, 베어메탈 / lab 환경이면 보통 비활성
    - { role: kubernetes-apps/policy_controller, tags: policy-controller }    # Kubernetes Policy 계열 컨트롤러, 예) PodSecurity Admission, OPA / Gatekeeper 연계
    - { role: kubernetes-apps/ingress_controller, tags: ingress-controller }  # Ingress Controller 설치, 예) NGINX Ingress, HAProxy Ingress, Traefik
    - { role: kubernetes-apps/external_provisioner, tags: external-provisioner } # 외부 스토리지 프로비저너, 예) CSI provisioner, NFS external provisioner, Ceph RBD / CephFS
    - { role: kubernetes-apps, tags: apps } # CoreDNS (이미 설치됐지만 재확인), Metrics Server, Local Path Provisioner, Helm add-ons

- name: Apply resolv.conf changes now that cluster DNS is up
  hosts: k8s_cluster      # 모든 노드
  gather_facts: false
  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
  environment: "{{ proxy_disable_env }}"
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/preinstall, when: "dns_mode != 'none' and resolvconf_mode == 'host_resolvconf'", tags: resolvconf, dns_late: true }