You are currently viewing M1맥북에서 Rocky리눅스에 UTM으로 HA Kubernetes 클러스터 구축하기

M1맥북에서 Rocky리눅스에 UTM으로 HA Kubernetes 클러스터 구축하기

1. Intro


처음 Kubernetes를 공부할때는 Windows 환경에서 VirtualBox + Vagrant를 사용해 로컬 쿠버네티스 클러스터를 생성해 이것저것 테스트 했었다.


비용부담없는 로컬 클러스터라는게 꽤나 마음에 들었다. 그러나 업무상 주로 사용하는 M1 Macbook에서는 VirtualBox를 사용할 수 없다. 대안으로 컨테이너 기반의 노드를 생성한 후 클러스터를 제공하는 Docker Desktop 내장 Kubernetes, Minikube, Kind, k3d, Orbstack 등의 도구들이 존재한다. 해당 도구들은 설치 전과정이 자동화 되어있어 몇가지 config를 던지면 클러스터가 뚝딱하고 나오는건 좋았지만.. 구성 과정등 세세한 부분을 살펴보기엔 아쉬운 부분이 있었다.


240121 현재시점에서 M1(ARM64)환경의 VirtualBox는 Test Builds 단계로만 존재한다.

macOS/ARM64용 테스트 빌드는 원칙적으로 M1 또는 M2 CPU가 있는 Mac에서 작동합니다. 그러나 이 빌드는 모든 최신 게스트 운영 체제에서 알려진 문제(심각한 성능 문제 포함)가 있는 개발자 미리보기입니다.

The test builds for macOS/ARM64 work in principle on Macs with M1 or M2 CPU. However, they are developer previews with known issues (including serious performance ones) with all recent guest operating systems.

https://www.virtualbox.org/wiki/Testbuilds


161031의 7.0 branch macOS/ARM64 BETA 이후로 추가 개발은 진행 예정에 없다.

7.0 브랜치에는 더 이상의 M1 패키지가 없을 것입니다. 왜냐하면 우리는 ARM에 대한 중요한 수정/향상을 백업하지 않기 때문입니다. 그래서 실질적인 진전이 없는 패키지를 구축하고 출시하는 데 시간을 투자하는 것은 의미가 없습니다. M1에서 VBox용 개발자 미리보기를 실행하려면 M1에 대한 수정/향상이 많은 최신 테스트 빌드를 확인할 수 있습니다(성능 향상은 기대하지 마십시오).

There won’t be any further M1 packages on the 7.0 branch as we don’t backport any significant fixes/enhancements for ARM there, so it doesn’t make much sense to invest time on building and releasing a package which will not see any real progress. If you want to run the developer preview for VBox on M1 you can check out the latest testbuilds which has a lot of fixes and enhancements for M1 (just don’t expect any performance improvement).

https://www.virtualbox.org/ticket/21771


가상머신의 기반의 로컬클러스터는 M1 맥북에서 구현할 수 없구나.. 하고 아쉬워 하던 도중, M1 맥북에서는 UTM을 통해서 가상 머신을 구동할 수 있다는 이야기를 들었다. Mac App Store UTM에서는 유료로 판매되고 있었기 때문에 오픈소스는 아니구나.. 했었다. 하지만 나중에 Github 레포지토리를 발견하고, 설치페이지 하단을 자세히 찾아보니 오픈소스버전과 앱스토어 에디션간에 기능상 차이는 없으며, 앱스토어에서는 자동업데이트를 할 수 있고, 개발자들에 대한 후원의 의미로 금액이 있다는 것을 확인했다.

맥 앱스토어 버전의 차이점은 무엇입니까?
UTM은 항상 완전 무료 오픈 소스입니다. 맥 앱스토어 버전은 무료 버전과 동일하고 무료 버전에서 빠지는 기능이 없습니다. 맥 앱스토어 버전의 유일한 장점은 자동 업데이트를 받을 수 있다는 것입니다. 앱스토어 버전을 구매하면 직접 UTM 개발 자금을 지원하고 지원을 보여줍니다.

What’s the difference in the Mac App Store version?

UTM is and always will be completely free and open source. The Mac App Store version is identical to the free version and there are no features left out of the free version. The only advantage of the Mac App Store version is that you can get automatic updates. Purchasing the App Store version directly funds the development of UTM and shows your support .

https://mac.getutm.app/


UTM을 사용해 생각했던 로컬 클러스터를 구축할 수 있게 되었다. 구축하면서 단순히 싱글 마스터노드 + 다수워커노드의 구조가 아닌, Keepalived + HAproxy를 활용한 고가용성 (HA, High Availability) 클러스터를 구성하고자 하였다. 런타임으로는 힙하게 docker 대신 containerd를 사용하고, CNI로는 Calico를 사용할 것이다.


2. UTM으로 Rocky Linux 노드생성하기

Rocky linux base ISO 다운로드


오픈소스의 주요 리눅스 OS는 Ubuntu가 있다. 그러나 CentOS7으로 리눅스를 배웠어서 그쪽동네가 조금더 친숙하다. CentOS은 곧(240630) 종료기간이 다가오기도하고, 대안으로 많이 떠오르는 Rocky Linux를 사용하여 노드를 구성할 것이다.


Download Rocky Page에서 Rocky 8,9의 base ISO를 다운로드 받을 수 있다. Rocky9의 ARM64 / minimal 이미지를 받아서 설치를 진행한다. (Rocky8의 경우 호환성 문제가 있어 진행할 수 없다.)

UTM 설치 및 실행

UTM 설치 페이지 또는 UTM Github의 Release에서 .dmg 파일을 다운로드 받아서 설치한 뒤 실행한다.

가상머신 생성 및 Rocky 설치

UTM 초기화면에서 새 가상머신 만들기 클릭 후 Virtualize를 선택한다. Emulate 옵션을 선택할 경우 ARM 대신 다른 CPU아키텍쳐(x86)의 OS를 설치 할 수 있으나 속도가 느리다는 단점이 있다. 다른 아키텍쳐를 택할 이유가 없으므로 Virtualize 옵션으로 설치를 한다.


운영체제에서 Linux를 선택한 뒤, Boot ISO Image 옵션에 Rocky-9.3-aarch64-minimal.iso 형식의 iso를 선택하여 부트이미지를 선택한다. k8s 노드의 최소사양은 2Core CPU, 2GB RAM(2048MB)이다(Kubeadm requirement). 그외의 값은 기본값으로 설정한다.

생성된 Linux 노드


이제 중앙의 > 버튼을 클릭하여 가상머신을 시작한다. Install Rocky 9.3을 선택한 뒤, 잠시 대기 후, 설치 UI를 통해 설치를 마무리한다. 본인의 경우 설명의 간소화를 위해 자동 파티션 선택, root(PW:admin123)와 사용자 계정에 암호(nasir/admin123) 설정과 root권한 허용 옵션만 주었다. 간단한 비밀번호를 경고문구가 뜨지만 완료를 두번 누르면 진행할 수 있다. 설치시작을 눌러 시작하고 마무리되면 머신을 종료한다.

Rocky9 설치 화면


UTM에서 CD/DVD를 초기화하여 부팅디스크를 제거하고, 머신을 우클릭하여 필요한 대수만큼 복제(Clone)한다. 이 글에서는 3대의 마스터와 2대의 워커를 구성하기 위해 총 5대를 생성했다. 머신간 유사설정도 있지만 개별적으로 설정해야할 부분도 있기 때문에 설치 이후 복제를 먼저 수행했다.

  • 작업 오류시 다른 VM을 복사해서 처음부터 설치수행X
  • 설치과정에 익숙하면 공통사항을 수행한 뒤 복제하여 작업해도 무방함
  • 복제이후 설정>네트워크>MAC addr 재설정
부트 이미지 제거 및 VM Clone

고정 IP 할당 및 호스트네임 설정

각 VM마다 개별적으로 IP를 설정해준다. nmcli/nmtui 명령어를 통해 설정할것이다.

Static IP설정에 있어, CentOS는 /etc/sysconfig/network-scripts를 사용했지만 rocky는 해당 설정이 비어있고 NetworkManager(NMcli/tui)를 사용하라는 Readme가 남아있다.

# cat /etc/sysconfig/network-scripts/readme-ifcfg-rh.txt

NetworkManager stores new network profiles in keyfile format in the
/etc/NetworkManager/system-connections/ directory.

Previously, NetworkManager stored network profiles in ifcfg format
in this directory (/etc/sysconfig/network-scripts/). However, the ifcfg
format is deprecated. By default, NetworkManager no longer creates
new profiles in this format.

Connection profiles in keyfile format have many benefits. For example,
this format is INI file-based and can easily be parsed and generated.

Each section in NetworkManager keyfiles corresponds to a NetworkManager
setting name as described in the nm-settings(5) and nm-settings-keyfile(5)
man pages. Each key-value-pair in a section is one of the properties
listed in the settings specification of the man page.

If you still use network profiles in ifcfg format, consider migrating
them to keyfile format. To migrate all profiles at once, enter:

# nmcli connection migrate

This command migrates all profiles from ifcfg format to keyfile
format and stores them in /etc/NetworkManager/system-connections/.

Alternatively, to migrate only a specific profile, enter:

# nmcli connection migrate <profile_name|UUID|D-Bus_path>

For further details, see:
* nm-settings-keyfile(5)
* nmcli(1)

rocky에서 네트워크 설정은 /etc/NetworkManager/system-connections/ 에 존재한다

ll /etc/NetworkManager/system-connections/

total 4
-rw-------. 1 root root 283 Mar 23 13:58 enp0s1.nmconnection

sudo cat /etc/NetworkManager/system-connections/enp0s1.nmconnection

[connection]
id=enp0s1
uuid=7f5d50f0-ada0-3eb0-bea1-b59d12ebafa9
type=ethernet
autoconnect-priority=-999
interface-name=enp0s1
timestamp=1711169216

[ethernet]

[ipv4]
address1=192.168.67.11/24,192.168.67.1
dns=8.8.8.8;
method=manual

[ipv6]
addr-gen-mode=eui64
method=auto

[proxy]

MacBook의 가상화 네트워크에있어, shared network는 vmnet framework를 사용한다. VM Network 대역대 정보들 과 같은 구성 정보는 /Library/Preferences/SystemConfiguration/com.apple.vmnet.plist 에서 조회가 가능하다

# 현재 vm대역대와 Netmask 조회
# https://github.com/utmapp/UTM/issues/3294
sudo cat /Library/Preferences/SystemConfiguration/com.apple.vmnet.plist | grep -A 3 Shared_Net_Address

#	<key>Shared_Net_Address</key>
#	<string>192.168.67.1</string>
#	<key>Shared_Net_Mask</key>
#	<string>255.255.255.0</string>

nmcli는 cli를 통한 네트워크 설정이 가능하고, nmtui는 tui(Terminal UI)를 통한 설정이 가능하다. 시각화된 nmtui를 사용하였다.

  • Edit a connection

처음 생성된 IP를 가지고 적절한 IP대역을 부여한다. 본인의 경우

IP: 192.168.66.9 > 머신당 192.168.66.11~15

의 설정을 줄것이다.

초기 네트워크 확인 및 고정 IP설정
  • Activate a connection

연결된 NIC(enp0s1)을 한번 down/up을 수행하여 변경된 설정이 적용되도록한다.

  • Set system hostname

서버별로 master1,2,3/worker1,2로 설정한다. sudo hostnamectl set-hostname master1 같은 cli 명령어로도 설정할 수 있지만 하는김에 같이한다.


이후 exec bash로 한번 쉘을 초기화 한 뒤, 바뀐 네트워크 설정을 확인하고 인터넷 연결이 되는지 다음의 명령어들을 실행한다.

exec bash
ip a
curl google.com
고정IP, Hostname 설정

나중에추가하는 nmcli

매번할때마다 gui로 하기 귀찮아서 cli도 정리

# 장치 정보 확인
nmcli

# 정보입력
nmcli connection modify enp0s1 ipv4.method manual ipv4.address 192.168.67.11/24 gw4 192.168.67.1 ipv4.dns 8.8.8.8

# 딸-깍
nmcli con down enp0s1 ; nmcli con up enp0s1

복제이후 vm 종료후 설정>네트워크>MAC addr 재설정

SSH 키 복사 및 VM Headless Start 설정

설치 및 일부 초기 설정은 UTM 콘솔화면으로 진행하였지만 해당 콘솔에 복사/붙여넣기등을 수행하려면 별도의 Guest설정이 필요하다. 하지만 귀찮다. 별도 콘솔이 생성되지 않도록 설정하고, ssh키를 붙여넣어 추후 설정은 iTerm에서 ssh로 가상머신에 붙어서 작업할 것이다.

맥북 iTerm에서 아래의 명령어를 통해 ssh키를 VM으로 전달한다. 이제 iTerm에서 간단하게 alias를 통해 VM에 접근할 수 있다,

# ssh key 생성
ssh-keygen -t rsa -f ~/.ssh/k8s-test -N ""

# 각 VM에 ssh 키 전달
ssh-copy-id -i ~/.ssh/k8s-test.pub nasir@192.168.66.11
ssh-copy-id -i ~/.ssh/k8s-test.pub nasir@192.168.66.12
ssh-copy-id -i ~/.ssh/k8s-test.pub nasir@192.168.66.13
ssh-copy-id -i ~/.ssh/k8s-test.pub nasir@192.168.66.14
ssh-copy-id -i ~/.ssh/k8s-test.pub nasir@192.168.66.15

# (선택) 접근 편의를 위한 alias 설정
vi ~/.zshrc
# 아래 구문 zsh 설정에 추가
alias r1='ssh nasir@192.168.66.11'
alias r2='ssh nasir@192.168.66.12'
alias r3='ssh nasir@192.168.66.13'
alias r4='ssh nasir@192.168.66.14'
alias r5='ssh nasir@192.168.66.15'


종료된 가상머신을 우클릭하여 설정에서 Display를 제거한다. 이제 UTM에서 부팅시 별도 콘솔을 생성하지않고 머신에 접근할 수 있다.


3. HA 마스터노드 구성

먼저 컨트롤노드 3대에 대해서, Keepalived 패키지로 고정된 진입점 VIP(Virtual IP)를 생성한다. 그 뒤 HAproxy로 해당 VIP로 들어오는 요청을 각 VM으로 분산할 것이다.

Keepalived

keepalived는 대표적인 VRRP(Virtual Redundancy Routing Protocol) 도구 중 하나이다.

keepalived 설정을 통해 가상의 라우터를 생성할 수 있다. 해당 라우터에 각 장비마다 갖는 실제 Real IP외에 가상의 VI(VIP)를 할당할 수 있으며, 설정의 priority(우선순위) 값에 따라 Master/Slave가 결정된다.

설치는 sudo yum install keepalived로 설치하며, /etc/keepalived/keepalived.conf 경로에 각 마스터/슬레이브별로 설정파일을 넣어준뒤, 마스터에 가상의 인터페이스가 생성되는지를 확인한다.

# keepalived 설치 및 활성화
sudo yum install keepalived -f
sudo systemctl enable keepalived.service --now
sudo systemctl status keepalived.service

# 설정 - 마스터
vi /etc/keepalived/keepalived.conf

vrrp_instance VI_1 {
    state MASTER
    interface eno1
    virtual_router_id 51
    priority 200
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.66.100
    }
}

# 설정 슬레이브 / 기타 슬레이브는 더 낮은 priority값 부여
vrrp_instance VI_2 {
    state BACKUP # Master 대신 BACKUP
    interface eno1
    virtual_router_id 51
    priority 100 # Master 보다 낮은값
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.66.100
    }
}

# 서비스 재시작
sudo systemctl restart keepalived.service

# 마스터(master1)에서 NIC확인
ip a | grep eno1

HAProxy

HAProxy는 소프트웨어 로드 밸런서로, 실제 물리적 로드밸런서대신 프로그램을 통해 로드밸런싱을 수행한다. 단순 트래픽 균등 분배 외에도, Health Check를 통해 실패한 서버로의 트래픽을 제외함으로써 고가용성을 제공한다.

HAProxy 설정은 크게 default/frontend/backend로 나누어 볼 수 있다. default 블럭을 통해 프로그램 자체의 기본값을 설정할 수 있고, frontend 블럭을 통해 어디에서 받는 트래픽을 뒷단으로 전달할지, backend 블럭을 통해 연결된 frontend의 요청을 어떻게 처리할지 설정할 수 있다.

# haproxy 설치 및 활성화
sudo yum install haproxy -f
sudo systemctl enable haproxy.service --now
sudo systemctl status haproxy.service

# 설정 - 마스터 노드간 동일
vi /etc/haproxy/haproxy.cfg

defaults
    maxconn 20000
    mode    tcp
    option  dontlognull
    timeout http-request 10s
    timeout queue        1m
    timeout connect      10s
    timeout client       86400s
    timeout server       86400s
    timeout tunnel       86400s

frontend k8s-api
    bind 192.168.66.100:16443
    mode tcp
    default_backend k8s-api

backend k8s-api
    option  httpchk GET /readyz HTTP/1.0
    option  log-health-checks
    http-check expect status 200
    mode tcp
    balance roundrobin
    default-server verify none check-ssl inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 5000 maxqueue 5000 weight 100
    server k8s-master1 192.168.66.11:6443 check fall 3 rise 2
    server k8s-master2 192.168.66.12:6443 check fall 3 rise 2
    server k8s-master3 192.168.66.13:6443 check fall 3 rise 2

# 서비스 재시작
sudo systemctl restart keepalived.service

# 확인 (k8s 설치전에는 해당 포트에 떠있는 서비스가 없기에 별도조회값 없음)
nc -v 172.19.0.100:16443
netstat -nltp | grep 16443


Master Node 사전 설정

전체적으로 Installing kubeadm 문서의 필요내용을 수행한다.

Hosts 설정

서버간 호출을 위해 다른 서버의 hostname 등록이 필요하다.

# 각 서버 마 /etc/hosts 수정
sudo vi /etc/hosts
192.168.66.11 master1
192.168.66.12 master2
192.168.66.13 master3
192.168.66.14 worker1
192.168.66.15 worker2

Swapoff

각 노드별로 swap memory 설정을 비활성화한다.

# 스왑확인
swapon -s
# swap off & 설정해제
sudo swapoff -a && sudo sed -i '/swap/s/^/#/' /etc/fstab

sysctl 설정

IPv4 전달과 iptables이 트래픽을 보기위해 아래의 설정을 추가한다. (참고링크)

노드 재부팅시 HAProxy cannot bind socket 에러 해결을 위해 ip_nonlocal_bind를 적용한다 (참고 링크)

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# sysctl params required by setup, params persist across reboots
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
net.ipv4.ip_nonlocal_bind           = 1
EOF

# Apply sysctl params without reboot
sudo sysctl --system
sudo systemctl restart systemd-modules-load.service

# 설정 확인
lsmod | grep br_netfilter
lsmod | grep overlay
sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward net.ipv4.ip_nonlocal_bind

SELinux 수정

SELinux(Security-Enhanced Linux)는 접근 제어 및 보안 강화를 위한 리눅스의 기능이다.

공식문서의 경우 사실상 비활성화인 Permissive mode를 권장하고 있다. (Red Hat-based distributions 부분의 설명 참고)

network plugin같은 컨테이너가, host의 filesystem에 접근하기위해 비활성화 필요하다.

방화벽처럼, SELinux를 활성화하고 필요기능을 제어하는식으로도 활용 가능하다.

# Set SELinux in permissive mode (effectively disabling it)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

# SELinux 상태보기
sestatus

방화벽(firewalld) 비활성화

kubernetes는 iptables를 사용해 통신한다. CentOS7 이후부터는 네트워크 패킷제어를 위해 iptables 대신 firewalld가 기본탑재되었다. iptables와 firewalld를 같이 사용할 시 네트워크 충돌 및 장애를 쉽게 경험할 수 있다. SELinux 컨트롤 까진 못해도 방화벽 포트제어 정도는 하고 싶었는데..

sudo systemctl disable firewalld.service --now
sudo systemctl status firewalld.service

컨테이너 런타임(Containerd) 설치

dockershim 제거 이후 cri-dockerd 등 CRI 표준을 따르게 수정한 도커런타임도 사용가능하다. 그러나 EKS는 기본적으로 Containerd을 사용하고 있기에 해당런타임을 설치해보았다.

Containerd 공식레포의 설치 방법 중, containerd binary 설치로 진행하였다. containerd와 runc의 충돌을 방지하기 위해서는 별도의 cgroup 설정이 필요하다. 공식문서

# containerd 설치
wget -q https://github.com/containerd/containerd/releases/download/v1.6.25/containerd-1.6.25-linux-arm64.tar.gz
sudo tar Cxzvf /usr/local containerd-1.6.25-linux-arm64.tar.gz

# systemd 등록
sudo mkdir -p /usr/local/lib/systemd/system
sudo curl -o /usr/local/lib/systemd/system/containerd.service https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
sudo systemctl status containerd.service

# runc 설치
wget -q https://github.com/opencontainers/runc/releases/download/v1.1.10/runc.arm64
sudo install -m 755 runc.arm64 /usr/local/sbin/runc

# CNI 플러그인 설치
wget -q https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-arm64-v1.3.0.tgz
sudo mkdir -p /opt/cni/bin
sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-arm64-v1.3.0.tgz

# Containerd PATH 등록
echo "export PATH=$PATH:/usr/local/bin" >> ~/.bash_profile

# cgroup driver 설정
sudo mkdir -p /etc/containerd
containerd config default > ~/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' ~/config.toml
sudo cp ~/config.toml /etc/containerd/config.toml
sudo systemctl restart containerd.service

kubeadm, kubelet and kubectl 설치

k8s 클러스터 구성 자동화 도구인 kubeadm, 각 노드 에이전트인 kubelet, 클러스터와 상호작용하기위한 cli인 kubectl을 설치한다.

기존 블로그등에는 k8s 레포에 대한 gpg key 등이 유효하지 않은 경우가 있었다. 언제나 공식문서를 참고할 것 Installing kubeadm, kubelet and kubectl

특정 버전을 fix해서 설치하고자 하는 경우 레포가 활성화된 경우 url 변경으로 가능하다(240121 시점 v1.26.11 까지). 그 이하의 경우 Without a package manager 부분 참고

# k8s yum repo 추가
# This overwrites any existing configuration in /etc/yum.repos.d/kubernetes.repo
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF

# 패키지 설치
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable --now kubelet

Kubeadm init

Kubeadm은 클러스터 구성 자동화 도구이다. cli도구나 별도의 config파일을 통해 클러스터 구성을 시작할 수 있다. 단순 테스트 구성은 명령줄로도 가능하나 세세한 설정과 기록목적에서 별도 config 파일을 활용할 수 있다.

# Calico에서 기본사용하는 pod-network대역은 192.168.0.0/16 > VM 대역이랑 겹치므로 172.16.0.0/16 대역으로 변경
sudo kubeadm init --control-plane-endpoint "192.168.66.100:16443" \
  --upload-certs --pod-network-cidr="172.16.0.0/16"

# kubeadm-config.yaml

# https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/
# https://github.com/ccleouf66/kubeadm-config/blob/681ca99259a834c431d31e8b4864c59b147787e7/kubeadm.md?plain=1#L119
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
localAPIEndpoint:
  advertiseAddress: 192.168.66.100
  bindPort: 16443
nodeRegistration:
  criSocket: unix:///var/run/containerd/containerd.sock
  imagePullPolicy: IfNotPresent
  name: master1
  taints: null
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
clusterName: local-k8s
kubernetesVersion: 1.28.0
controlPlaneEndpoint: "192.168.66.100:16443"
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
  podSubnet: 172.16.0.0/16
etcd:
  local:
    dataDir: /var/lib/etcd
apiServer:
  timeoutForControlPlane: 4m0s
imageRepository: registry.k8s.io
certificatesDir: /etc/kubernetes/pki
controllerManager: {}
dns: {}
scheduler: {}

kubeadm init 하고나면 다음과같이 cluster 조인을 위한 토큰값을 포함한 명령어가 떨어진다. 다른 글들에서는 해당 명령어를 잃어버리면 다시는 못찾으니 잘 기록하라고 되어있지만.. 이세계에 안되는게 어딨어? + 그러면 초기 클러스터 생성 이후 노드 추가는 더 못하는건가? 당연히 방법이 있고, 해당 값 재생성/조회하는 방법은 worker노드 추가시 설명하겠다.

CNI 설정

CNI는 국내 사용량이 제일 많(을것이라고 추정되)는 Calico를 적용한다. 기본 manifest에서의 pod network 대역은 192.168.0.0 이다. EKS환경이 아니기에 별도의 IP4_POOL 값을 적용해서 Node대역과 Pod 대역을 별도로 가져가야한다.

Install Calico networking and network policy for on-premises deployments

# Get default config
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -O


# Change default IPV4POOL_CIDR
sed -i 's/# - name: CALICO_IPV4POOL_CIDR/- name: CALICO_IPV4POOL_CIDR/' calico.yaml
sed -i 's/#   value: "192.168.0.0\/16"/  value: "172.16.0.0\/16"/' calico.yaml

# Install Calico
kubectl apply -f calico.yaml 

# 클러스터 구성 확인
kubectl get node # READY 확인
kubectl get all -A

master1의 노드상태가 Ready이고, CNI 및 다른 컴포넌트 컨테이너가 정상적으로 구성되었으면 Controlplane 구성이 마무리 되었다.


4. 워커노드 구성과 Kubeadm join

worker node 사전 설정

위의 Master Node 사전 설정 부분 중 keepalived+haproxy 부분을 제외하고, kubeadm init 전까지의 내용을 수행한다.

  • Hosts 설정
  • Swapoff
  • sysctl 설정
  • SELinux 수정
  • 방화벽(firewalld) 비활성화
  • 컨테이너 런타임(Containerd) 설치
  • kubeadm, kubelet and kubectl 설치

kubeadm join

kubeadm 을 통한 클러스터 구성시, node join을 위해 인증서 복사시 토큰/인증서 해시값을 통해 인증한다. 그러나 토큰의 유효기간은 24시간이여서, 추후로 노드를 추가할 시 새로 토큰을 발급 받아야한다.

1. 토큰 생성

# 발급된 토큰 확인
kubeadm token list

# 토큰 생성
kubeadm token create

2. (컨트롤플레인의 노드에서) discovery-token-ca-cert hash 값 조회

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

3. Node join 명령어 생성

sudo kubeadm join <api-server endpoint> –token <token> –discovery-token-ca-cert-hash sha256:<hash>

sudo kubeadm join 192.168.66.100:16443 --token r25wa1.9k46k1bzroe0x6z5 	--discovery-token-ca-cert-hash sha256:faba0abcc06cf800e6498036cad263525cdde11d030a5b1908dad5fe6a117b8d


이후 마스터 노드에서 admin 인증권한을 가진 /etc/kubernetes/admin.conf 파일을 호스트(맥북)의 ~/.kube/config로 복사한다. 이로써 k8s 클러스터 구성이 완료되었다.


5. Outro

이로써 맥북 환경에 UTM을 사용하여 Keepalived+Haproxy로 고가용성이 확보된 Controlplane과 2대의 Worker 노드로 구성된 Kubernetes 클러스터를 생성하였다.


k8s 클러스터를 생성할때 노드에서는 어떤 설정이 필요한지, 통신과정을 살펴보기에는 좋았다. 그러나 다음과 같은 이유로 자주 사용은 못하고 있다.

  • UTM에는 VM Snapshot 기능이 별도로 제공되지않는다. 테스트 후 망가지면 수동원복
  • 여러대의 클러스터환경 번거로움. 버전간 차이/CNI차이등을 테스트하기 위해 2개이상의 클러스터가 필요한 경우 어.. 사실 하려면 할수있겠는데, 앞서 언급하였던 컨테이너 기반의 k8s 클러스터 생성도구가 간편해서 그쪽을 더 활용하고 있다.


또한 On-Prem 환경 기준으로 Keepalived+Haproxy로 HA를 구성하였으나 설정 내용을 생각해보니 이제는 Nginx로 다할수 있지않을까? 라는 생각이 들었다. 다음엔 Nginx로 HA해봐야지


참고자료

https://jinmook.tistory.com/15
https://assets.velcdn.com/@skybluelion/Kubernetes-UTM%EC%9C%BC%EB%A1%9C-ubuntu-server-%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%84%B8%ED%8C%85
https://velog.io/@skybluelion/Kubernetes-UTM%EC%9C%BC%EB%A1%9C-ubuntu-server-%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%84%B8%ED%8C%85

Leave a Reply