본문 바로가기
Infra/Kubernetes

01. Kubernetes 설치 및 구성

by de.v_gom 2025. 6. 30.

Kubernetes 설치 및 구성

집에 남는 컴퓨터가 있어 쿠버네티스 학습을 위해 구성했다. PC 사양은 아래와 같다. 

사양 라이젠 5500GT / RAM 32 GB / SSD 512
OS  Rocky 9 minimal 
구성 1  kube-master : 192.168.254.11
구성 2 kube-master : 192.168.254.4

0 단계; 사전설정 (모든 노드)

쿠버네티스 설치를 위해 먼저 방화벽과 Swap, SELinux 설정을 비활성화 해야한다.

이는 master node, worker node (각각의 서버) 에서 모두 적용되어야 한다.

단계 0.1; SELinux 비활성화

SELinux(Security Enhanced Linux) : 리눅스 커널에 내장된 보안모듈로 정책 기반의 접근제어 제공
- 권한(chmod, chwon, sudo 등) 외에 프로세스가 어떤 자원에 어떻게 접근할 수 있는지 세밀하게 제어

 

SELinux 는 보안 컨텍스트에 따라 파일을 제어하게 되는데 containerd 또는 CNI(Container Network Interface) 가 정책에 의해 네트워크 또는 파일에 접근이 불가능할 수 있다. 특히 오류가 직접적으로 SELinux 때문이라는 힌트가 없어 디벙깅에 어려움이 있으므로 비활성화 하는 것을 권장한다.

SELinux 는 아래와 같이 3가지 모드가 있으므로 참고하길 바란다.

모드 설명 특징
Enforcing 정책 강제 적용 정책 위반 시 차단 + 로그
Permissive 정책 미적용, 경고 차단은 안하고 로그만 남김 (디버깅용)
Disabled SELinux 완전 비활성화 커널 수준에서 SELinux 사용 안 함

여기서는 Permissive 모드로 사용할 예정이다.

setenforce 0 
sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
  1. setenfoce 0 : SELinuxe 모드를 Permissive 모드로 즉시 전환
  2. sed -i … : /etc/selinux/config 파일 내에서 SELINuX=enfocing 설정을 SELINUX=permissive 로 변경

즉시, 영구 적용을 위해 위 명령어를 입력한다.

0.2 단계; Firewalld 비활성화

Firewalld : 리눅스에서 제공하는 기본적인 방화벽
- 네트워크 트래픽에 대한 허용과 차단 정책 관리

 

일반적으로 Kubernetes 는 노드간 통신, 포트 개방, Pod-CNI 네트워크 등이 필요로 하지만 방화벽이 이를 차단할 수 있다. 특히 CNI 또는 kube-proxy 가 사용하는 포트를 허용하지 않으면 Pod 간 통신이 불가능해 서비스가 동작하지 않을 수 있어 해당 포트들을 허용해 주어야 한다.

먼저 네트워크 트래픽 문제를 단순화하기 위해서 방화벽을 비활성화 해 주고 하나씩 정책을 적용해 나가는 방법이 좋을 수 있다. 아래는 방화벽을 비활성화 하는 명령어이다.

systemctl disable --now firewalld

아래 Kubernetes 에서 사용하는 기본 포트들은 접근 가능하도록 설정하고 나머지는 비활성하고 시작할 수도 있다.

firewall-cmd --permanent --add-port=6443/tcp # API Server
firewall-cmd --permanent --add-port=10250/tcp # kubelet 
firewall-cmd --permanent --add-port=30000-32767/tcp # Node Port
firewall-cmd --reload

여기서는 방화벽을 disable 하고 진행하겠다.

0.3 단계; Swap 비활성화

Swap : RAM 이 부족할 경우 디스크 공간을 임시 메모리처럼 사용하는 영역
- 주 메모리 이 부족한 경우 사용하지 않는 메모리 페이지를 Swap 공간으로 옮겨 주 메모리 확보
- 현재 필요한 적업에 주 메모리를 우선 사용하도록 함
- 주 메모리가 부족할 때 시스템의 다운을 막아주어 안정성을 확보할 수 있지만 보조 메모리르(디스크)를 사용하기 때문에 주 메모리보다는 느려 성능 저하가 발생할 수 있다.

 

Kubernetes 에서는 각 Pod 가 메모리를 얼마나 쓰는지 정확히 추적해야 한다. 하지만 Swap 메모리가 활성화 되어 있으면 실제 메모리 사용량이 적게 보일 수 있어 스케줄링이나 OOM(Out of Memory) 처리가 잘못될 수 있다. 그리고 Pod가 느려지는 원인을 찾기 어렵게 만드는 역할을 하기도 한다. 아래 명령어를 이용해 Swap 을 비활성화 하자

swapoff -a
sed -i '/swap/d' /etc/fstab
  • swapoff -a : swap 즉시 비활성화
  • sed -i '/swap/d' /etc/fstab : /etc/fstab(시스템 부팅시 마운트할 파일 시스템 설정 파일) 에서 swap이 포함된 라인을 삭제

이렇게 삭제한 swap 는 free 명령어를 이용해서 확인 가능하다.

free -h 
               total        used        free      shared  buff/cache   available
Mem:            30Gi       627Mi        30Gi       8.0Mi       212Mi        29Gi
Swap:             0B          0B          0B

0.4 단계; /etc/hosts 등록

K8S 를 설치할 때 IP 기반으로 설치를 하면 IP 가 변경되는 상황(예: DHCP 가 새로운 IP 를 할당)이 발생한 경우 설정값에 의해 동작이 제대로 되지 않을 수 있다. 이 경우 되돌리기가 매우 힘들어 먼저 호스트 이름과 IP 를 수동으로 연결하는 설정을 해 주면 된다

  • /etc/hosts : IP 와 호스트 이름(도메인)을 수동으로 연결하는 설정파일로 DNS 보다 우선순위가 높다. 실 운영환경에서는 해당 파일보다는 DNS 가 효율적일 수 있다 (상황에 맞춰서 설정하면 된다)
# hosts 파일 열기
vim /etc/hosts

# 아래 내용 입력 
192.168.254.11   kube-master
192.168.254.4    kube-worker

0.5 단계; 커널 파라미터 조정

Kubernetes 와 CNI 플러그인이 Pod 간 통신, IP 포워딩, iptables 사용을 원할하게 하기 위한 필수 커널 설정을 하겠다. 먼저 커널 모듈을 로드하기 위한 명령어이다.

modprobe overlay 
modprobe br_netfilter
  • modprobe overlay : containerd 등 컨테이너 런타임이 사용하는 OverlayFS 파일 시스템을 위한 커널 모듈을 로드한다.
  • modprobe br_netfilter : 브릿지 네티워크에서 iptables 로 패킷 필터링이 가능하게 해주는 모듀을 로드한다. 이는 Pod 통신에 필수요소이다.

이렇게 모듈을 로드할 수 있으나 위 설정은 재부팅시 사라지게 된다. 따라서 아래 명령어를 통해 부팅시 자동으로 로드할 수 있도록 설정할 수 있다.

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

이제 네트워크 관련된 커널 파라미터를 설정하자

tee /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-iptables  = 1   # 브릿지 네트워크에서도 iptables 룰 적용. 즉, iptables 를 이용해 Pod 간 통신 및 NAT 처리가 가능해짐
net.bridge.bridge-nf-call-ip6tables = 1   # IPv6 용 iptables 적용
net.ipv4.ip_forward                 = 1   # IP 포워딩 허용 (라우팅/Pod 통신용)
EOF

IP 포워딩을 허용해 주는 이유는 시스템이 라우터 처럼 IP 패킷을 다른 인터페이스로 전달하기 위해서이다.

  • Node → Pod
  • Pod → Pod
  • Pod → 외부

해당 값이 0이면 Node/Pod 간 통신이 차단되어 CNI 오류 또는 통신 불능 상황이 발생하게 된다.

이제 아래 명령어를 이용해 설정을 커널에 반영하자

# /etc/sysctl.d/*.conf 에 정의된 모든 설정을 커널에 즉시 반영 
sysctl --system

0.6 단계; hostname 변경 (선택)

해당 단계는 서버에 접근한 경우 hostname 이 없거나 해당 서버가 정확히 어디인지 알기 쉽게 하기 위해 설정해 준다.

# 적용방법 1 : hostname-ctl 명령어 이용
$ hostnamectl set-hostname "새로운 호스트 이름"

# 적용방법 2 : hostname 설정파일 수정 
# 
# hostname 입력 
# - master node : kube-master 
# - worker node : kube-worker 
# 
# hostname 은 한 줄만 입력한다. 따라서 각 노드에 맞춰 위 이름을 입력해주면 된다. 
# 여기서는 Master node 를 입력했다
$ echo "hostname" > /etc/hostname

# 재시작 하면 적용이 완료 된다. 
$ reboot

이렇게 적용하면 프롬프트에 아래와 같이 출력될 것이다

  • PS1 설정에 따라서 안보여질 수 있음. 이 경우 PS1 형식 설정을 변경해주면 확인 가능
[root@kube-master ~]#

기본적인 설정이 끝났으니 이제 본격적인 k8s 설치를 진행해보자

1 단계; Kubernetes 설치 (모든 노드)

Kubernetes 를 설치하기 위해서는 먼저 컨테이너 런타임을 설치해야하며 그다음으로 Kubernetes 패키지를 설치해야 한다. 그러기 위해 아래 설치 절차를 진행하자.

1.1 단계; RPM 저장소 를 이용한 containerd 설치

먼저 오케스트레이션 시스템에서 사용할 컨테이너 런타임을 설치해야 한다. 이때 표준처럼 널리 자리잡고 있는것이 containerd 이다. 1.0 이전에는 CRI(Container Runtime Interface) 호환을 위한 별도의 패키지를 설치해 주어야 했지만 1.1 이후 CRI 플러그인이 내장되어 있다.

containerd 는 컨테이너 오케스트레이션 시스템을 위한 경량화된 고수준 컨테이너 런타임이다.
- containerd 의 아키텍처와 핵심 기능

 

containerd 를 설치하기 위한 필수 패키지를 설치하겠다. 먼저 containerd.io 공식 저장소를 등록하기 위해서 yum-utils 를 설치한 후 저장소를 등록하겠다.

$ dnf install -y yum-utils
# docker-ce 저장소 등록 CentOS/RHEL/Rocky 호환 저장소이며, containerd.io 포함 
$ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

저장소가 등록되었다면 containerd 를 위한 필수 패키지를 설치하겠다.

$ dnf install -y epel-release containerd

containerd 패키지를 설치 했다면 다음과 같이 기본 설정파일을 생성하고 Kubernetes 호환을 위한 SystemdCgroup을 설정하고 containerd 를 활성화/실행한다.

$ mkdir -p /etc/containerd
$ containerd config default | tee /etc/containerd/config.toml
$ sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml 
$ $ystemctl enable --now containerd
  • containerd config default | tee /etc/containerd/config.toml : containerd 의 기본 설정 템플릿을 생헝하고 /etc/containerd/config.toml 에 파일로 출력한다. 이 파일에서 containerd 의 동작 방식을 커스터마이징 할 수 있다.
  • sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml : Systemd 를 cgroup 드라이버로 사용하도록 설정을 변항하는 것으로 Kubernetes 에서는 이를 권장하여 containerd 또한 맞춰서 설정해 주어야 한다. 이 설정이 다르면 kubeletcontainerd 와 충돌할 수 있다.

systemd : 리눅스 시스템에스 프로세스를 부팅, 관리, 추적하는 초기화 시스템 (Chat GPT)
- 프로세스를 관리하는 데몬

cgroup(Control Gorups) : 리눅스 커널 기능으로 프로세스 그룹에 대해 CPU, Memory, Disk, Network 등의 자원 사용을 제한하거나 모니터링하는 기능 (Chat GPT)
- 프로세스 단위가 아닌 그룹 단위로 자원을 제어함
- Kubernetes 는 Pod(컨테이너의 집합) 마다 cgroup 을 통해 자원 사용 제한

1.2 단계; Kubernetes 패키지 설치

K8s 를 설치하는 방법에 여러가지가 있지만 여기서는 kubeamd 를 이용한 방법을 선택했다. 만약 kubespray 를 이용해소 손쉽게 설치하고 싶다면 아래의 링크를 확인해서 설치해보자

먼저 k8s 를 사용하기 위해서는 kubeadm, kubelet, kubectl 이 3가지 패키지를 설치해야 한다.

  • kubeadm : kubernetes 클러스터를 설치하고 초기화하는 도구이다. 이를 이용해 마스터 노드를 초기화하고, 워커노드를 클러스터에 참여시킬 수 있으며, 인증서, 토큰, kubeconfig 등을 자동으로 생성해준다. 실제 클러스터의 구성을 제어하지는 않고 클러스터 설치 및 부트스트랩 역할을 주로 한다.
  • kubelet : 노드에서 실행되며, Pod를 생성/삭제/모니터링하기 위한 에이전트로 Kubernetes API 서버로부터 명령을 받아 컨테이너의 라이프사이클을 관리한다.
  • kubectl: 사용자가 직접적으로 Kubernetes API 서버에 명령을 내릴수 있는 CLI로 리소스의 생성, 삭제, 확인, 수정 등 대부분의 클러스터 작업에 사용된다.

자세한 것은 Kubernetes 를 학습하면서 정리하겠다.

부트스트랩 : 시스템을 실행 가능한 상태로 초기화하는 과정이며 여기서는 클러스터 구성을 자동으로 준비해주는 작업을 의미 (ChatGPT)

$ tee /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
EOF

$ dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
$ systemctl enable --now kubelet 

2 단계; Kubernetes Cluster

여기서 부터는 master node 와 worker node 에 대한 설정을 각각 해주어야 한다. 먼저 마스터 노드에서 클러스터를 초기화해서 네트워크 플러그인을 설치한 후 워커 노드를 클러스터에 참여해주어야 한다.

2.1 단계; kube-master 초기화 및 네트워크 플러그인 설치

먼저 마스터 노드에서 클러스터를 초기화 해야한다. 이때 pod 의 네트워크 블록을 설정해 주어야 한다.

$ kubeadm init --pod-network-cidr=10.0.0.0/16
... 

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.254.11:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash:sha256>

위 문구들이 출력되면 정상적으로 초기화 된 것이다. 이제 kubernetes 마지막으로 처리하라는대로 추가해보자

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# root 사용자 인 경우 아래 명령어로 설정 파일 경로를 지정할 수 있음 
export KUBECONFIG=/etc/kubernetes/admin.conf

이제 초기화 완료되었으니 CNI 플러그인을 설치하자 여기서는 Calico 를 설치했다.

$ kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
poddisruptionbudget.policy/calico-kube-controllers created
serviceaccount/calico-kube-controllers created
serviceaccount/calico-node created
serviceaccount/calico-cni-plugin created
configmap/calico-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgpfilters.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/caliconodestatuses.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrole.rbac.authorization.k8s.io/calico-cni-plugin created
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-cni-plugin created
daemonset.apps/calico-node created
deployment.apps/calico-kube-controllers created

이렇게 calico 까지 설치가 됐다면 마스터 노드는 설정이 완료 된 것이다.

2.2 단계; kube-worker 노드 클러스터 참여

앞서 master node 초기화 해서 출력된 명령어를 입력하자.

$ kubeadm join 192.168.254.11:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash:sha256>
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 500.643929ms
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

위와 같이 나오면 worker node 가 준비 완료 된 것이다.

만약 설치가 잘못되었다면 아래 명령어를 이용해서 삭제할 수 있다.

$ kubeadm reset

2.3 단계; kubernetes node 클러스터 확인

이제 클러스터가 정상적으로 설정됐는지 확인해 보자. 마스터에서 아래 명령어를 실행해서 Master 와 Worker 이 Ready 상태가 되면 된다.

$ kubectl get nodes
NAME          STATUS   ROLES           AGE    VERSION
kube-master   Ready    control-plane   12m    v1.30.14
kube-worker   Ready    <none>          2m4s   v1.30.14

참고