📝 작성 배경
인프런에서 진행 중인 워밍업 클럽 스터디에 참여하고 있는데, 이번 주 미션 중 하나는 “자신의 PC에 VM을 통해 Linux를 설치하고, 쿠버네티스를 직접 구성해보는 것”이었다. 미션을 진행하면서 여러 가지 문제들이 발생했는데, 이를 해결했던 방법들을 기록해두면 좋을 것 같아 이 글을 작성하게 되었다.
🚨 lima-vm
사용 방법은 공식 문서를 참고.
PC 환경
- OS: macOS 15.4.1 24E263 arm64
- CPU: Apple M3 Pro
- Memory: 36GB
k8s
- Version: v1.30
- CRI: containerd(1.7.27-3.1.el9)
- CNI: Flannel
Etc.
- VM: Lima-vm
- Linux: Rocky-9 (Red Hat 계열)
☸️ 쿠버네티스 설치
Rocky Linux 기본 설정
- 타임존 설정 확인
- 설정 명령어:
timedatectl set-timezone Asia/Seoul
- 확인 명령어:
timedatectl
1
2
3
4
5
6
7
8
| [crispin@lima-linux crispin]$ timedatectl
Local time: Sun 2025-06-01 18:00:11 KST
Universal time: Sun 2025-06-01 09:00:11 UTC
RTC time: Sun 2025-06-01 09:00:12
Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
|
- 타임존 확인 시 이미
Asia/Seoul
되어 있다면 별도의 설정을 해 줄 필요는 없다. 만약 다른 타임존으로 설정 되어 있다면 설정 명령어를 사용
Kubeadm 설치 전 사전작업
- 방화벽 해제 확인
- 설정 명령어:
systemctl stop firewalld && systemctl disable firewalld
- 확인 명령어:
systemctl status firewalld
1
2
| [crispin@lima-linux crispin]$ systemctl status firewalld
Unit firewalld.service could not be found.
|
lima-vm
을 사용해서 VM
을 띄우는 경우 별도의 설정이 없다면, 경량화된 이미지를 사용하기 때문에 별도의 firewalld
이 설정되어 있지 않다. 만약 다른 tool
을 사용하거나 미리 설정을 통해 방화벽을 설치 했다면 실습 편의상 방화벽을 해제 시키는 편이 편하다.- 메모리
swap
비 활성화 확인
- 설정 명령어:
swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab
- 확인 명령어:
free -h
, cat /etc/fstab | grep swap
free -h
명렁어
1
2
3
4
| [crispin@lima-linux crispin]$ free -h
total used free shared buff/cache available
Mem: 7.5Gi 406Mi 6.1Gi 8.0Mi 1.1Gi 7.1Gi
Swap: 0B 0B 0B
|
- 방화벽과 마찬가지로
lima-vm
을 사용하면 별도의 설정을 하지 않는다면, swap
메모리 설정이 되어 있지 않다.
CRI(container runtime interface) 설치
- iptables 세팅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# 필요한 sysctl 파라미터를 설정하면, 재부팅 후에도 값이 유지된다.
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
EOF
# 재부팅하지 않고 sysctl 파라미터 적용하기
sudo sysctl --system
|
- 확인 명령어:
cat /etc/modules-load.d/k8s.conf && cat /etc/sysctl.d/k8s.conf && lsmod | grep overlay && lsmod | grep br_netfilter
1
2
3
4
5
6
7
8
9
| [crispin@lima-linux crispin]$ cat /etc/modules-load.d/k8s.conf && cat /etc/sysctl.d/k8s.conf && lsmod | grep overlay && lsmod | grep br_netfilter
overlay
br_netfilter
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
overlay 176128 0
br_netfilter 32768 0
bridge 303104 1 br_netfilter
|
- containerd.id 설치
- docker repo 설정 명령어:
yum install -y yum-utils
, yum-config-manager --add-repo [https://download.docker.com/linux/centos/docker-ce.repo](https://download.docker.com/linux/centos/docker-ce.repo)
1
2
3
4
5
6
7
8
9
| [crispin@lima-linux crispin]$ sudo yum install -y yum-utils
Last metadata expiration check: 0:27:12 ago on Sun 01 Jun 2025 05:56:20 PM KST.
Package yum-utils-4.3.0-16.el9.noarch is already installed.
Dependencies resolved.
Nothing to do.
Complete!
[crispin@lima-linux crispin]$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
|
- docker repo 설정 확인 명령어:
yum repolist enabled
1
2
3
4
5
6
| [crispin@lima-linux crispin]$ yum repolist enabled
repo id repo name
appstream Rocky Linux 9 - AppStream
baseos Rocky Linux 9 - BaseOS
docker-ce-stable Docker CE Stable - aarch64
extras Rocky Linux 9 - Extras
|
- 설치 가능한 버전의 containerd.io 리스트 확인 명령어:
yum list containerd.io --showduplicates | sort -r
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| [crispin@lima-linux crispin]$ yum list containerd.io --showduplicates | sort -r
Rocky Linux 9 - Extras 14 kB/s | 17 kB 00:01
Rocky Linux 9 - BaseOS 2.3 MB/s | 2.5 MB 00:01
Rocky Linux 9 - AppStream 8.6 MB/s | 7.4 MB 00:00
Docker CE Stable - aarch64 704 kB/s | 71 kB 00:00
containerd.io.aarch64 1.7.27-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.26-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.25-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.24-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.23-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.22-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.21-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.20-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.19-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.7.18-3.1.el9 docker-ce-stable
containerd.io.aarch64 1.6.9-3.1.el9 docker-ce-stable
# ...
|
- containerd 설치 명령어:
yum install -y containerd.io-{version}
containerd
버전에 따른 k8s
호환성 정보 확인은 해당 페이지를 참고- 해당 포스팅에서는
1.7.27-3.1.el9
버전을 설치
- containerd 설치 확인 명령어:
systemctl status containerd
1
2
3
4
5
| [crispin@lima-linux crispin]$ systemctl status containerd
○ containerd.service - containerd container runtime
Loaded: loaded (/usr/local/lib/systemd/system/containerd.service; disabled; preset: disabled)
Active: inactive (dead)
Docs: https://containerd.io
|
- 여기서부터 뭔가 조금씩 달라지는 부분이 발생한다. 우선
Active
상태가 active (running)
상태가 아닌 inactive (dead)
상태로 되어 있다. 그리고 /usr/local/lib/systemd/system/containerd.service
가 disabled
로 되어 있다. - 위 두가지 부분에 대해 별도로 검색해보고 수정을 했어야 했다. 그럼 해결 방법을 보자.
inactive (dead)
상태- 원인:
containerd
가 실행되지 않은 상태로 별도의 명령어를 통해 실행시켜야 함. - 명령어
- containerd 실행 명령어:
sudo systemctl enable containerd
- 부팅 시 자동 시작 설정 명령어:
sudo systemctl enable containerd
disabled
상태- 원인: 서비스 파일이 일반적이 경로가 아닐 경우
systemd
가 인식하지 못하는 문제 - 명령어
- 데몬 리로드:
sudo systemctl daemon-reload
- 서비스 활성화:
sudo systemctl enable containerd.service
- 서비스 시작:
sudo systemctl start containerd.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| [crispin@lima-linux crispin]$ sudo systemctl daemon-reload
[crispin@lima-linux crispin]$ sudo systemctl enable containerd.service
Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service → /usr/local/lib/systemd/system/containerd.service.
[crispin@lima-linux crispin]$ sudo systemctl start containerd.service
[crispin@lima-linux crispin]$ sudo systemctl status containerd.service
● containerd.service - containerd container runtime
Loaded: loaded (/usr/local/lib/systemd/system/containerd.service; enabled; preset: disabled)
Active: active (running) since Sun 2025-06-01 18:32:59 KST; 4min 23s ago
Docs: https://containerd.io
Main PID: 15871 (containerd)
Tasks: 9
Memory: 14.0M
CPU: 441ms
CGroup: /system.slice/containerd.service
└─15871 /usr/local/bin/containerd
# ...
|
- 상태와 서비스가 활성화된 모습을 볼 수 있다.
CRI(container runtime interface) 활성화
- cri 설정 확인 활성화
- 활성화 명령어:
sudo sh -c 'containerd config default > /etc/containerd/config.toml && sed -i "s/ SystemdCgroup = false/ SystemdCgroup = true/" /etc/containerd/config.toml'
- 설정 확인 명령어:
cat /etc/containerd/config.toml
1
2
| [crispin@lima-linux crispin]$ cat /etc/containerd/config.toml | grep SystemdCgroup
SystemdCgroup = true
|
Kubeadm 설치
- repo 설정 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
# permissive 모드로 SELinux 설정(효과적으로 비활성화)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable --now kubelet
|
- 위 명령어를 통해
repo
를 설정 하는 경우
1
2
3
4
| Errors during downloading metadata for repository 'kubernetes':
- Status code: 404 for https://packages.cloud.google.com/yum/repos/kubernetes-el7-aarch64/repodata/repomd.xml (IP: xxx.xxx.xx.xxx)
Error: Failed to download metadata for repo 'kubernetes': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried
Failed to enable unit: Unit file kubelet.service does not exist.
|
- 해당 에러가 발생 한다. 에러가 발생하는 원인은 Google Cloud의 기존 Kubernetes 패키지 저장소가 deprecated되어서 발생하는 문제로 패키지 저장소의 링크를 변경해주면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| # 1. 기존 저장소 파일 제거
sudo rm -f /etc/yum.repos.d/kubernetes.repo
# 2. 새로운 저장소 추가
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/{version}/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/{version}/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
# 3. permissive 모드로 SELinux 설정(효과적으로 비활성화)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
# 4. 패키지 설치
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
# 5. kubelet 서비스 활성화
sudo systemctl enable --now kubelet
|
version
에 설치하고 싶은 k8s
버전을 기입하면 해당 버전으로 설치 할 수 있다.- 해당 포스팅에서는
v1.30
버전을 설치
- repo 설정 추가 확인
1
2
3
4
5
6
7
| [crispin@lima-linux crispin]$ yum repolist enabled
repo id repo name
appstream Rocky Linux 9 - AppStream
baseos Rocky Linux 9 - BaseOS
docker-ce-stable Docker CE Stable - aarch64
extras Rocky Linux 9 - Extras
kubernetes Kubernetes
|
- SELinux 설정 확인
- 설정 확인 명령어:
cat /etc/selinux/config | grep SELINUX && sestatus
1
2
3
4
5
6
7
8
9
10
11
12
13
| [crispin@lima-linux crispin]$ cat /etc/selinux/config | grep SELINUX && sestatus
SELINUX=permissive # 해당 설정 확인
SELINUXTYPE=targeted
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: permissive # 해당 설정 확인
Mode from config file: permissive # 해당 설정 확인
Policy MLS status: enabled
Policy deny_unknown status: allowed
Memory protection checking: actual (secure)
Max kernel policy version: 33
|
- kubelet, kubeadm, kubectl 패키지 설치 확인
- kubeadm 설치 확인 명령어:
kubeadm version
1
2
| [crispin@lima-linux crispin]$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"30", GitVersion:"v1.30.13", GitCommit:"50af91c466658b6a33d123fae8a487db1630971c", GitTreeState:"clean", BuildDate:"2025-05-15T09:55:10Z", GoVersion:"go1.23.8", Compiler:"gc", Platform:"linux/arm64"}
|
- kubelet 설지 확인 명령어:
systemctl status kubelet
1
2
3
4
5
6
7
8
9
10
| [crispin@lima-linux crispin]$ systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; preset: disabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: activating (auto-restart) (Result: exit-code) since Sun 2025-06-01 19:20:51 KST; 8s ago
Docs: https://kubernetes.io/docs/
Process: 18604 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS (code=exited, status=1/FAILURE)
Main PID: 18604 (code=exited, status=1/FAILURE)
CPU: 61ms
|
- kubectl 설지 확인 명령어:
kubectl version
1
2
3
4
| [crispin@lima-linux crispin]$ kubectl version
Client Version: v1.30.13
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
The connection to the server localhost:8080 was refused - did you specify the right host or port?
|
- 패키지 설정 확인 시
kubelet
이 지속적으로 재 실행 되는 부분이 확인 될 수 있는데 이건 kubeadm
이 아직 init
되지 않았기 때문에 발생하는 문제로 kubeadm init
이후 클러스터를 생성하면 해결된다. kubectl
에서 보이는 refused
이후 kubectl
에 추가적인 설정을 해주면 해결된다.- 위 두가지 부분에 대해서는 아래에서 다룰 예정이다.
Kubeadm 으로 클러스터 생성
- pod network 세팅
- 네트워크 인터페이스 확인 명령어:
ip route show && ip addr show
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| default via xxx.xxx.xxx.1 dev eth0 proto dhcp src {ip} metric 100
xxx.xxx.xxx.0/24 dev eth0 proto kernel scope link src xxx.xxx.xxx.xxx metric 100
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback xx:xx:xx:xx:xx:xx brd xx:xx:xx:xx:xx:xx
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd xx:xx:xx:xx:xx:xx
altname enxxxxxx
inet xxx.xxx.xxx.xxx/24 brd xxx.xxx.xxx.255 scope global dynamic noprefixroute eth0
valid_lft 3121sec preferred_lft 3121sec
inet6 xxxx:xxxx:xxxx::xxxx/64 scope link
valid_lft forever preferred_lft forever
|
- 클러스터 초기화
- 클러스터 초기화 명령어:
kubeadm init --pod-network-cidr={cidr} --apiserver-advertise-address {ip}
cidr
은 사용하고자 하는 CNI
에 대한 기본 값을 사용해도 되고 별도로 설정해줘도 된다.- 해당 포스팅에서는
Flannel
를 사용할 예정이며, Flannel
의 cidr
기본값은 10.244.0.0/16
이다.
- 클러스터 상태 확인
- 클러스터 상태 확인 명령어:
kubectl get node
1
2
3
| [crispin@lima-linux crispin]$ kubectl get node
E0601 20:04:33.891690 22662 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp [::1]:8080: connect: connection refused
The connection to the server localhost:8080 was refused - did you specify the right host or port?
|
- 클러스터 확인 시 위와 같은 에러가 발생하게되면 추가 설정이 필요하다.
- 설정 명령어
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
를 추가해주고 다시 명령어를 사용하면
1
2
3
| [crispin@lima-linux crispin]$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
lima-linux NotReady control-plane 2m48s v1.30.13
|
- 생성된
node
를 확인할 수 있다. node
가 NotReady
상태인 이유는 아직 CNI
플러그인이 설치되지 않았기 때문이며, 플러그인을 설치해주면 Read
상태로 변경된다.CNI
플러그인 설치 명령어
1
2
3
4
5
6
7
| [crispin@lima-linux crispin]$ kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
namespace/kube-flannel created
serviceaccount/flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
configmap/kube-flannel-cfg createdkubectl cluster-info dump | grep -m 1 cluster-cidr
daemonset.apps/kube-flannel-ds created
|
- 설치하고 1분정도 시간이 지난 이후에 다시
kubectl get nodes
명령어를 사용하면 Ready
상태로 변경된다.
1
2
3
| [crispin@lima-linux crispin]$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
lima-linux Ready control-plane 99s v1.30.13
|
- Master에 pod 를 생성 할 수 있도록 설정(Optional)
- 설정 명령어:
kubectl taint nodes {node_name} node-role.kubernetes.io/control-plane-
1
2
| [crispin@lima-linux crispin]$ kubectl taint nodes lima-linux node-role.kubernetes.io/control-plane-
node/lima-linux untainted
|
- 확인 명령어:
kubectl describe nodes | grep Taints
1
2
| [crispin@lima-linux crispin]$ kubectl describe nodes | grep Taints
Taints: <none> # 해당 설정 확인
|
쿠버네티스 편의 기능 설치
- kubectl 자동 완성 기능 추가
1
2
3
| source <(kubectl completion bash)
alias k=kubectl
complete -o default -F __start_kubectl k
|
- 해당 명령어를 사용하면
k get pods
처럼 kubectl
을 k
로 사용할 수 있고, 추가적으로 tap
을 통해 자동완성을 사용할 수 있다.
1
2
3
4
| [crispin@lima-linux crispin]$ k get nodes
NAME STATUS ROLES AGE VERSION
lima-linux Ready control-plane 18m v1.30.13
[crispin@lima-linux crispin]$ k get bash: _get_comp_words_by_ref: command not found
|
k
는 사용이 가능한데 자동완성을 위해 tap
을 눌렸을때 위 처럼 에러가 발생할 수 있는데 bash-completion
패키지가 설치되어 있지 않아 발생하는 문제로 해당 패키지를 설치하면 된다.- 패키지 설치 명령어:
sudo yum install -y bash-completion
1
2
| sudo yum install -y bash-completion # 명령어 설치
source /etc/profile.d/bash_completion.sh # 새로고침
|
- 해당 패키지 설치 이후 새로고침을 하면 자동완성을 사용할 수 있게 된다.
💡 마무리
현업에서 kubernetes
를 지금도 사용하고 있고, 자주 활용하고 있는데 직접 linux
에 kubernetes
를 설치한 경험은 처음이라 흥미로운 경험이였다. 매번 클라우드에서 제공되는 서비스를 통해서 사용하거나 이미 회사의 온프레미스 서버에 설치 되어 있는 kubernetes
를 사용하기만 했는데 직접 설치하고 사용해보니 생각보다 설치 과정이 쉽지 많은 않게 느껴졌다.
강의를 참고하고, 발생하는 에러는 검색을 하면서 진행했는데, 강의에서 안내해주는 tool
을 사용한것도 아니고 아키텍처도 다르다 보니 생각보다 오래걸리기는 했는데 설치하고 나서 pod
를 직접 띄우고 조회하면서 제대로 동작하는 모습을 보며 매우 뿌듯함을 느꼈다.
🔖 REFERENCE