플레이북 GitHub Repository
이 게시글은 다음의 작업에 대해 설명한다:
- Ansible 을 활용하여 쿠버네티스 클러스터의 필수 요소 설치를 자동화
containerd
kubelet
kubeadm
kubectl
- kubeadm으로 Control Plane 구성
- Cilium을 CNI(Container Network Interface) 플러그인으로 설치
- 마스터 자격 증명 가져오기
Ansible은 Red Hat의 오픈 소스 IaC도구로, 서버 자원의 구성 관리를 자동화하여 휴먼 에러를 줄이고, 생산성을 높여준다.
Ansible은 다음의 장점을 가진다:
- YAML기반으로 IaC(Infrastructure as Code) 실현
- 멱등성(Idempotence) - 여러 번 실행해도 크래시가 생기지 않는다
- 그러나, 이미 생성된 클러스터의 노드에 대해서 다시 실행하면, 오류가 발생할 수 있다
- 기본 필요사항들의 설치까지만 멱등성이 보장된다.
- 노드를 추가하고 싶을 시, 인벤토리에 기존 노드들은 제거하고 돌리시는게 좋다
🎬 Ansible 시작을 위한 기본 세팅
Ansible의 구성 요소는 다음과 같다:
controller
- 구성 대상이 되는 서버들에게 명령을 내리는 호스트
inventory
playbook
- 대상 서버들에 대한 명령집 파일
- playbook은 여러 개의 play들로 구성
- 각 play에는 1개 이상의 task로 구성됨
- 각 play마다 대상 host가 있음
Ansible은 대상 서버에서 작업을 취하기 위해, ssh로 접속하여 동작한다.
즉, Ansible이 접속할 수 있는 사용자를 대상 서버에서 만들어줘야 한다.
각 서버에 아래의 작업들을 해야 한다:
각 서버에 ansible 유저 생성
adduser로 사용자를 생성한다.
useradd 를 내부적으로 사용하며, 최초 비밀번호 초기화 등의 더 많은 기능들을 제공한다.
쿠버네티스 설치를 위해, 권한이 필요하다.
또한, 완전한 자동화를 위해 패스워드 입력을 생략시킬 것이다
1
2
3
4
5
6
7
|
#/etc/sudoers.d/ansible에서 아래와 같이 작성
ansible ALL=(ALL) NOPASSWD:ALL
# ansible 사용자는
# 모든 명령어를 sudo권한으로 사용가능하며
# 모든 명령어를 비밀번호 없이 사용가능하다
# 실제로는 최소권한을 주는 것이 낫다.
|
ssh 키 생성
(로컬, 즉 컨트롤러에서) ssh key를 생성한다.
1
2
3
4
5
6
|
ssh-keygen -N '' -f ~/.ssh/id_rsa -b 2048 -t rsa
# no passphrase
# save private key to ~/.ssh/id_rsa
# 2048 b
# type rsa
|
로컬에서 각 서버로 키 배포
1
|
ssh-copy-id -i <key-file> -p <port> ansible@<server-ip>
|
⚙️ ansible.cfg 및 inventory.ini 작성
Ansible은 /etc/ansible/ansible.cfg의 값을 기본적으로 참조하지만,
현재 작업 디렉토리에서의 ansible.cfg가 있다면, 그 설정 파일을 우선으로 읽는다.
ansible.cfg는 ansible명령을 실행할 때의 환경 설정 파일이다.
inventory.ini는 대상 호스트들의 정보가 담겨있는 파일이다.
ansible.cfg 예시
1
2
3
4
5
6
7
8
9
10
|
[defaults]
inventory = ./inventory.ini # inventory.ini가 쓸 인벤토리 파일임을 명시
remote_user = ansible # ssh 유저는 ansible
aks_pass = false # 패스워드 묻지 않음(SSH키 이용)
[privilege_escalation]
become = true # 권한 상승 허용
become_method = sudo # 권한 상승 방법은 sudo(sudo > su)
become_user = root # 권한 상승되어 root로서 동작
become_ask_pass = false # 권한 상승 비밀번호를 묻지 않음. true로 두면 과정에서 비밀번호 물음
|
inventory.ini 예시
1
2
3
4
5
6
7
8
9
|
[masters]
master ansible_host=192.168.64.10 ansible_port=22
[workers]
worker1 ansible_host=192.168.64.12 ansible_port=22
[k8s-nodes:children] # 호스트 그룹 상속
masters
workers
|
또는, ~/.ssh/config 파일에서 정리해두면, 해당 hostname을 간단하게 쓸 수 있다
🚀 Ansible을 이용한 모든 노드들에 대해 필수 구성요소 설치
본격적으로 쿠버네티스를 설치한다.
각 노드들은 ubuntu 24.04를 기준으로 한다.
준비사항
사전 확인 할것들은 다음과 같다:
- 2GB이상 RAM
- 2코어 이상의 CPU/vCPU
- 클러스터 전체는 네트워크에 연결되어야 함(퍼블릭/프라이빗 상관없음)
- MAC주소 및
product_uuid가 모두 고유해야 함
ip link 또는 ifconfig -a로 MAC주소 확인
sudo cat/sys/class/dmi/id/product_uuid로 product_uuid확인 가능
hostname이 모두 고유해야 함
- 시간이 알맞게 동기화되어있어야 함
- 네트워크 어댑터가 2개 이상인 경우, 신경써야 함
- 스왑 비활성화(kubelet이 제대로 작동하려면 스왑은 꺼야 함) → 플레이북에서 자동화한다
- 6443포트가 방화벽에 막히지는 않는지 확인해야 함(kube-apiserver 위해)
iptables/firewalld, 보안 그룹 등
- cni에 따라 추가로 필요한 게 있을 수 있음
순서
- 시스템 업데이트 및 업그레이드
- NTP서버 동기화(현재 플레이북에 없음)
- ufw비활성화(현재 플레이북에 없음)
- 스왑 제거
- 커널 설정 변경(
br_netfilter, overlay, iptables, forwarding)
br_netfilter는 브릿지 네트워크 패킷을 iptables에서 볼 수 있게 해준다.
overlay는 컨테이너의 파일 시스템인 overlayfs를 사용할 수 있도록 해준다
net.ipv4.ip_forward로 ipv4 패킷 포워딩을 활성화한다.
net.bridge-nf-call-iptables, net.bridge-nf-call-ip6tables을 활성화하여 리눅스 브릿지를 통과하는 IPv4 및 IPv6 트래픽이 iptables의 규칙을 통과하도록 해준다
- 컨테이너 런타임 설치(containerd)
containerd는 Docker로부터 제공받을 수 있다
containerd 초기 설정 + cgroup설정(SystemdCgroup=true) + 재시동
systemd와 통합하여 리소스 제한 및 격리를 관리시킨다
kubelet, kubeadm, kubectl 설치
- 버전 고정
kubelet 실행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
---
- name: Install Kubernetes essentials on all nodes
hosts: k8s_nodes
become: true
gather_facts: true
vars:
docker_repo_codename: '{{ ansible_lsb.codename | default(''noble'') }}' # noble: 24.04
k8s_minor: v1.34
k8s_keyring: /etc/apt/keyrings/kubernetes-apt-keyring.gpg
k8s_list: /etc/apt/sources.list.d/kubernetes.list
containerd_pause_image: registry.k8s.io/pause:3.10.1
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Upgrade all packages to the latest version
apt:
upgrade: dist
- name: Disable swap temporarily
command: swapoff -a
when: ansible_swaptotal_mb | default(0) > 0
- name: Disable swap permanently
ansible.posix.mount:
name: none
fstype: swap
state: absent
- name: Ensure kernel modules are enabled (br_netfilter, overlay)
modprobe:
name: '{{ item }}'
state: present
persistent: present
loop:
- br_netfilter
- overlay
- name: Enable iptables and forwarding via sysctl config
sysctl:
name: '{{ item }}'
value: "1"
reload: yes
sysctl_file: /etc/sysctl.d/k8s.conf
state: present
sysctl_set: yes
loop:
- net.bridge.bridge-nf-call-ip6tables
- net.bridge.bridge-nf-call-iptables
- net.ipv4.ip_forward
- name: Install required packages
apt:
name:
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: yes
- name: Ensure keyrings directory exists
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
- name: Download Docker GPG key
get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/keyrings/docker.asc
mode: "0644"
- name: Convert Docker GPG key to dearmored keyring
command: /usr/bin/gpg --dearmor -o /etc/apt/keyrings/docker.gpg /etc/apt/keyrings/docker.asc
args:
creates: /etc/apt/keyrings/docker.gpg
- name: Add Docker APT repository
apt_repository:
repo: deb [arch={{ (ansible_architecture == 'aarch64') | ternary('arm64', 'amd64' )}} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ docker_repo_codename }} stable
state: present
filename: docker
- name: Update apt and install containerd.io
apt:
name: containerd.io
state: present
update_cache: yes
## cgroup
- name: Setup containerd config directory
ansible.builtin.file:
path: /etc/containerd
state: directory
- name: Generate default containerd config
command: containerd config default
register: containerd_config
- name: Write containerd config.toml
copy:
dest: /etc/containerd/config.toml
content: '{{ containerd_config.stdout }}'
- name: Set SystemdCgroup=true in containerd
replace:
path: /etc/containerd/config.toml
regexp: SystemdCgroup = false
replace: SystemdCgroup = true
- name: Ensure CRI sandbox_image matches kubeadm expected pause image
replace:
path: /etc/containerd/config.toml
regexp: ^\s*sandbox_image\s*=\s*".*"\s*$
replace: ' sandbox_image = "{{ containerd_pause_image }}"'
- name: Restart and enable containerd
systemd:
name: containerd
state: restarted
enabled: true
- name: state the kubernetes keyring file
stat:
path: '{{ k8s_keyring }}'
register: k8s_keyring_stat
- name: Download Kubernetes apt signing key
get_url:
url: https://pkgs.k8s.io/core:/stable:/{{ k8s_minor }}/deb/Release.key
dest: /tmp/k8s_key.gpg
mode: "0644"
timeout: 30
- name: Ensure temporary GNUPG home for dearmor
file:
path: /etc/apt/keyrings/gnupg
state: directory
mode: "0700"
owner: root
group: root
- name: Dearmor Kubernetes Release.key to keyring (idempotent)
command: |
/usr/bin/gpg --dearmor --yes --batch --no-tty --output {{ k8s_keyring }} /tmp/k8s_key.gpg
environment:
GNUPGHOME: /etc/apt/keyrings/gnupg
args:
creates: '{{ k8s_keyring }}'
- name: Ensure permissions on Kubernetes keyring
file:
path: '{{ k8s_keyring }}'
mode: "0644"
- name: Configure Kubernetes apt repository
copy:
dest: '{{ k8s_list }}'
mode: "0644"
content: |
deb [signed-by={{ k8s_keyring }}] https://pkgs.k8s.io/core:/stable:/{{ k8s_minor }}/deb/ /
- name: apt-get update for Kubernetes repo
apt:
update_cache: yes
- name: Install kubelet, kubeadm, kubectl for {{ k8s_minor }}
apt:
name:
- kubelet
- kubeadm
- kubectl
state: present
- name: Hold kube packages (no unintended upgrades)
command: apt-mark hold kubelet kubeadm kubectl
changed_when: false
- name: Enable kubelet
systemd:
name: kubelet
enabled: true
state: started
|
설치하기
k8s-install.yml을 실행하여 플레이북을 실행한다
1
|
ansible-playbook k8s-install.yml
|
🕹️ Control Plane 초기화
단일 Control Plane 구성
kubeadm init으로 Control Plane을 초기화한다
1
|
kubeadm init --pod-network-cidr=10.43.0.0/16
|
조금만 기다리면, 아래와 같은 결과가 나온다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.0.4:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
|
HA구성
(미완, 추후 추가예정)
🛜 네트워크 플러그인 설치
쿠버네티스에는 여러 네트워크 플러그인이 존재합다
- Flanner
- Calico
- Cilium
- 기타(클라우트 환경 CNI 등)
여기서는 떠오르고 있는 CNI인 Cilium을 설치합다.
Cilium은 L7정책, 네트워크 가시성, 서비스 메시 등의 기능들을 강력히 제공합다
기존 kube-proxy제거
1
|
kubectl delete kube-proxy -n kube-system
|
cilium CLI를 이용한 설치
Linux 기준
1
2
3
4
5
6
7
|
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
|
1
|
cilium install --version 1.18.1
|
helm을 이용한 설치
cilium CLI 대신, helm으로 설치도 가능하다
1
2
3
|
helm install cilium cilium/cilium --version 1.18.1 \
--namespace kube-system \
--set kubeProxyReplacement=true # kube-proxy를 대체하기
|
설치 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
root@ubuntu:~# cilium status --wait
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: OK
\__/¯¯\__/ Hubble Relay: disabled
\__/ ClusterMesh: disabled
DaemonSet cilium Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium-envoy Desired: 1, Ready: 1/1, Available: 1/1
Deployment cilium-operator Desired: 1, Ready: 1/1, Available: 1/1
Containers: cilium Running: 1
cilium-envoy Running: 1
cilium-operator Running: 1
clustermesh-apiserver
hubble-relay
Cluster Pods: 2/2 managed by Cilium
Helm chart version: 1.18.1
Image versions cilium quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9: 1
cilium-envoy quay.io/cilium/cilium-envoy:v1.34.4-1754895458-68cffdfa568b6b226d70a7ef81fc65dda3b890bf@sha256:247e908700012f7ef56f75908f8c965215c26a27762f296068645eb55450bda2: 1
cilium-operator quay.io/cilium/operator-generic:v1.18.1@sha256:97f4553afa443465bdfbc1cc4927c93f16ac5d78e4dd2706736e7395382201bc: 1
root@ubuntu:~#
|
연결 테스트하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
cilium connectivity test
ℹ️ Monitor aggregation detected, will skip some flow validation steps
✨ [kubernetes] Creating namespace cilium-test-1 for connectivity check...
✨ [kubernetes] Deploying echo-same-node service...
✨ [kubernetes] Deploying DNS test server configmap...
✨ [kubernetes] Deploying same-node deployment...
✨ [kubernetes] Deploying client deployment...
✨ [kubernetes] Deploying client2 deployment...
⌛ [kubernetes] Waiting for deployment cilium-test-1/client to become ready...
⌛ [kubernetes] Waiting for deployment cilium-test-1/client2 to become ready...
⌛ [kubernetes] Waiting for deployment cilium-test-1/echo-same-node to become ready...
⌛ [kubernetes] Waiting for pod cilium-test-1/client-64d966fcbd-72mrh to reach DNS server on cilium-test-1/echo-same-node-65dd6bdb5c-dl4g8 pod...
⌛ [kubernetes] Waiting for pod cilium-test-1/client2-5f6d9498c7-4cdpn to reach DNS server on cilium-test-1/echo-same-node-65dd6bdb5c-dl4g8 pod...
⌛ [kubernetes] Waiting for pod cilium-test-1/client-64d966fcbd-72mrh to reach default/kubernetes service...
⌛ [kubernetes] Waiting for pod cilium-test-1/client2-5f6d9498c7-4cdpn to reach default/kubernetes service...
⌛ [kubernetes] Waiting for Service cilium-test-1/echo-same-node to become ready...
⌛ [kubernetes] Waiting for Service cilium-test-1/echo-same-node to be synchronized by Cilium pod kube-system/cilium-rl58t
⌛ [kubernetes] Waiting for NodePort 192.168.0.4:32364 (cilium-test-1/echo-same-node) to become ready...
ℹ️ Skipping IPCache check
🔭 Enabling Hubble telescope...
⚠️ Unable to contact Hubble Relay, disabling Hubble telescope and flow validation: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:4245: connect: connection refused"
ℹ️ Expose Relay locally with:
cilium hubble enable
cilium hubble port-forward&
ℹ️ Cilium version: 1.18.1
🏃[cilium-test-1] Running 123 tests ...
(중략)
.........
✅ [cilium-test-1] All 74 tests (295 actions) successful, 49 tests skipped, 0 scenarios skipped.
|
👬 Worker Node 조인시키기
kubeadm init의 결과로, 아래와 같은 부분이 있는데, 이를 Worker Node에서 실행하면 된다
1
2
|
kubeadm join <Control-Plane>:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
|
🔑 관리자 자격 증명 가져오기
context를 내 로컬(개인 PC 및 노트북)에 가져오면, 원격으로 kubectl명령어를 사용할 수 있다.
context는 클러스터 정보 및 자격 증명의 조합 을 말합다.
처음 kubectl을 사용하는 경우
kubeadm init의 결과로, 아래와 같은 부분이 나왔을 겁니다.
즉, Control Plane의 /etc/kubernetes/admin.conf를 자신의 ~/.kube/config로 가져오면 된다.
scp등을 이용할 수 있다.
1
2
3
|
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
기존 config에 context를 추가하는 경우
우선, admin의 설정 파일을 로컬로 가져온다
1
|
scp <remote-user>:<control-plane>:/etc/kubernetes/admin.conf ~/admin.conf
|
그 뒤, 기존 설정 파일과 가져온 설정파일을 병합한다
1
|
KUBECONFIG=~/.kube/config:~/admin.conf kubectl config view --flatten > ~/merged; mv ~/merged ~/.kube/config
|
(Optional)컨텍스트의 이름을 변경할 수 있다.
1
|
kubectl config rename-context kubernetes-admin@kubernetes <새 컨텍스트 이름>
|
💻 CLI 도구 모음