▶︎ 구축 환경
구축 환경
- Ubuntu 22.04 환경을 기준으로 구축
- CRI(Container Runtime Interface):
containerD
- CNI(Container Network Interface):
calico
최소 사양
- 메모리: 2Gib 이상
- CPU: 2GHZ 이상
- 머신 간 네트워크 연결 필수
▶︎ 구성도
구성도의 경우 3대의 Ubuntu 22.04 서버를 기반으로 Master 1대, Node 2대로 구성
▶︎ 설치 방법
‣ Master & Worker
- 마스터와 워커 노드에서 1~8번 작업 진행
1. 패키지 최신화
패키지를 최신 버전으로 갱신 후 리부팅
sudo apt-get update -y && sudo apt-get upgrade -y
2. 타임존 설정
sudo timedatectl set-timezone Asia/Seoul
3. hosts 설정
sudo bash -c "echo -e '192.168.219.132 k8s-node1\n192.168.219.142 k8s-node2' >> /etc/hosts"
4. Swap 해제
# 스왑 비활성화
sudo swapoff -a && sudo sed -i '/\/swap.img\|\/swap/s/^/#/' /etc/fstab
🤷 swap off 하는 이유
쿠버네티스를 설치하는 글들을 보다보면 swap memory 비활성화
대목은 꼭 들어가있다.
swap memory
는 물리 메모리(RAM)의 용량이 부족할 때 하드 디스크의 일부 공간을 메모리처럼 사용하는 것의 기능을 한다.
스왑 메모리를 켜놔도 나쁠건 없어 보이는데
쿠버네티스 클러스터를 구축할 때에는 왜 꺼놓아야 하는걸까?
쿠버네티스의 컴포넌트인 kubelet
은 이러한 상황을 처리하도록 구현되지 않았기 때문이다.
쿠버네티스는 Pod를 생성할 때, 필요한 만큼의 리소스를 할당 받아서 사용하는 구조인데
이 때, 메모리 swap을 전혀 고려하지 않고 설계되었기 때문에 클러스터 노드들은 모두 swap 메모리를 비활성화 해줘야 한다.
스왑 기능은 본래 가용된 메모리보다 더 큰 메모리 할당을 가능하도록 하기 위함이고,
쿠버네티스는 주어진 노드의 자원을 100%에 가깝게 사용하는 것이 목적이기에 스왑 메모리 사용은 노드 자원이 일관되지 않기 때문에
쿠버네티스에서 swap off
를 권장하고 있는 것이다.
애초에 swap off
를 하지 않으면 kubelet
이 정상 실행되지 않기 때문에 쿠버네티스를 사용할 수 없다.
kubelet
의 기본 동작에서 노드에서 스왑 메모리가 감지되면 시작에 실패하기 때문이다.
# 스왑 메모리를 활성화 했을 때 kubelet 로그
journalctl -u kubelet.service | grep 'swap'
May 07 04:45:30 k8s-master kubelet[1371]: E0507 04:45:30.439443 1371 run.go:74] \
"command failed" err="failed to run Kubelet: running with swap on is not supported, \
please disable swap! or set --fail-swap-on flag to false. /proc/swaps contained: [Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority /swap.img\t\t4194300\t\t0\t\t-2]"
그러나 쿠버네티스 v1.22
이상 버전부터 리눅스용 스왑 기능을 알파 단계로 도입하였다.
리눅스 사용자는 스왑 메모리 지원을 노드 단위로 활성화 할 수 있으나, cgroup v2에 대한 부적절한 지원, 불충분한 메트릭 요약 및 API 통계, 부적절한 테스트 등을 포함한 많은 문제 등이 발생하였다.
https://kubernetes.io/blog/2021/08/09/run-nodes-with-swap-alpha/
v1.28
이상 버전부터 swap을 사용하기 위한 베타 버전을 지원하고 있고
알파 단계와 비교했을 때 스왑이 활성화된 실행에 대해 kubelet
은 알려진 단점들을 많이 보완했다고 한다.
적용 방법은 아래 문서를 참고해보도록 하자.
https://kubernetes.io/blog/2023/08/24/swap-linux-beta/
5. SELinux 비활성화
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
6. iptable 설정
- bridge 네트워크를 통해 송수신되는 패킷(컨테이너 패킷)이 iptables 설정에 따라 제어되도록 설정 해야함.
- 관련 링크
# 부팅 시에 overlay와 br_netfilter 라는 두 가지 모듈을 로드하도록 설정한다.
cat << EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
# overlay 커널 모듈을 즉시 로드. 컨테이너 오버레이 파일 시스템에 사용됨.
sudo modprobe overlay
# br_netfilter 커널 모듈을 즉시 로드. Linux 브리지 네트워크와 관련된 네트워크 필터링에 사용됨.
sudo modprobe br_netfilter
# 여기서는 몇 가지 중요한 파라미터를 설정. 예를 들어,
# net.bridge.bridge-nf-call-iptables는 iptables가 브리지 트래픽을 처리할 수 있도록 하는 설정임.
# 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
EOF
# 설정했던 값을 적용.
sudo sysctl --system
정상적으로 설정되었는지 확인
$ lsmod | grep "^br_netfilter"
# Module Size Used by
br_netfilter 28672 0
$ lsmod | grep overlay
# Module Size Used by
overlay 151552 54
# 다음의 세가지가 모두 1 로 되어 있는 지 확인
$ sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
7. k8s 도구 설치
4-1. kubernetes repository 추가 (Deprecated
)
sudo apt -y install curl apt-transport-https net-tools
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg|sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/kubernetes.gpg
4-1. kubernetes repository 추가 (변경된 주소
)
# 최신 버전 사용 시 v1.n로 변경
# https://kubernetes.io/blog/2023/08/15/pkgs-k8s-io-introduction/
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.27/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.27/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
4-2. kubeadm, kubelet, kubectl 패키지 설치
### https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/
## 레포 버전 갱신
sudo apt update
## 설치 가능한 kubeadm, kubelet, kubectl 버전 확인
apt search kubeadm
apt search kubelet
apt search kubectl
## kubeadm, kubelet, kubectl 설치
sudo apt-get -y install kubeadm=1.27.13-2.1 kubelet=1.27.13-2.1 kubectl=1.27.13-2.1 --allow-downgrades
sudo apt-get update
## 버전 고정 (각각의 버전이 달라지면 안됨)
sudo apt-mark hold kubelet kubeadm kubectl
💥 버전을 고정하는 이유?
kubeadm은 kubelet 또는 kubectl을 설치/관리를 하지 않으므로, kubeadm이 설치하려는 쿠버네티스의 Control Plane의 버전과 일치하는지 확인해야 한다.
버전을 맞추지 않을 경우 예상치 못한 오류로 이어질 수 있는 버전 차이(skew)가 발생할 위험이 크다.
4-3. 설치 확인
$ kubectl version --client && kubeadm version
WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short. Use --output=yaml|json to get the full version.
Client Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.3", GitCommit:"25b4e43193bcda6c7328a6d147b1fb73a33f1598", GitTreeState:"clean", BuildDate:"2023-06-14T09:53:42Z", GoVersion:"go1.20.5", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v5.0.1
kubeadm version: &version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.6", GitCommit:"741c8db18a52787d734cbe4795f0b4ad860906d6", GitTreeState:"clean", BuildDate:"2023-09-13T09:19:54Z", GoVersion:"go1.20.8", Compiler:"gc", Platform:"linux/amd64"}
8. CRI 설치
쿠버네티스는 3가지의 **도커 컨테이너 인터페이스(CRI)**를 지원한다.
- Docker / Docker-Shim (kubernetes 1.27 버전 이상부터 지원)
- CRI-O
- Containerd
이 중에서 원하는 컨테이너 런타임(CRI)을 설치하면 되는데 필자의 경우 containerd로 설치하도록 하겠다.
8-1. Docker CE
- Docker CE로 설치하는 경우
1-1 쿠버네티스 컨테이너 런타임을 위해 도커 설치
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
1-2 도커 구동 확인
docker ps
1-3 컨테이너의 cgroup 관리에 systemd를 사용하도록 도커 데몬 구성
sudo mkdir /etc/docker 2>/dev/null
cat << EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
1-4 도커 재시작, 부팅 시 자동시작 되도록 설정
$ sudo systemctl enable docker
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
8-2. ContainerD
- Containerd로 설치하는 경우
2-1 containerd 설치
# 도커 레포지토리 설정
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker-archive-keyring.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# containerd 설치
sudo apt-get update -y
sudo apt-get install -y containerd.io
# containerd 재시작
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
2-2 CRI 활성화
# defualt cgroupfs에서 systemd로 변경 (kubernetes default는 systemd)
sudo bash -c "containerd config default > /etc/containerd/config.toml"
sudo sed -i 's/ SystemdCgroup = false/ SystemdCgroup = true/' /etc/containerd/config.toml
2-3 containerd 재시작
sudo systemctl restart containerd
‣ 마스터 노드
- 마스터 노드에서만 9~13번 작업 진행
9. master 초기화
1. kubelet 서비스를 enable로 변경
sudo systemctl enable --now kubelet
2. 이미지 다운로드
# CRI 를 Containerd 로 설치했을 경우
sudo kubeadm config images pull --cri-socket unix:///run/containerd/containerd.sock
# CRI 를 Docker 로 설치했을 경우
sudo kubeadm config images pull --cri-socket unix:///run/cri-dockerd.sock
3. 클러스터 초기화 (Pod Network 설정)
sudo kubeadm init --pod-network-cidr=172.24.0.0/24 --apiserver-advertise-address 192.168.219.110
sudo kubeadm token create --print-join-command > ~/join.sh
옵션 | 설명 |
---|---|
--pod-network-cidr=172.24.0.0/24 |
이 플래그는 Kubernetes에서 Pod 네트워크의 CIDR (Classless Inter-Domain Routing)를 설정합니다. 이것은 클러스터 내에서 파드 간 통신을 관리하는 IP 주소 범위를 정의합니다. 여기서는 172.24.0.0/24 로 설정되었습니다. |
--apiserver-advertise-address 192.168.219.110 |
이 플래그는 API 서버가 사용할 IP 주소를 설정합니다. Kubernetes API 서버는 클러스터의 상태를 관리하고 클라이언트와 통신하는데 사용됩니다. 여기서는 192.168.219.110 으로 쿠버네티스 마스터 서버 IP가 설정되었습니다. |
10. kubectl 설정
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
11. Calico 설치
- 컨테이너 네트워크 인터페이스(CNI) 설치 작업
- 최신 버전 확인 - relases page
- 설치 Docs
Calico
Calico는 컨테이너 오케스트레이션 시스템인 Kubernetes와 같은 컨테이너 오케스트레이션 플랫폼에서 네트워크 정책 및 네트워킹 솔루션을 제공하는 오픈 소스 프로젝트입니다. Calico는 컨테이너화된 애플리케이션의 네트워크 요구 사항을 관리하고, 가상 머신 (VM) 및 물리적 서버와 통합하여 대규모 컨테이너 인프라스트럭처에서 확장 가능하고 안전한 네트워킹을 제공합니다
1. Calico 프로젝트에서 Operator와 Custom resource 다운로드 받기
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/tigera-operator.yaml
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/custom-resources.yaml
2. Operator를 클러스터에 설치
kubectl create -f tigera-operator.yaml
3. custom-resources.yaml
파일에 기재된 CIDR를 #9-master-초기화의 3번 항목에 설정했었던 CIDR 주소로 변경
sed -ie 's/192.168.0.0\/16/172.24.0.0\/24/g' custom-resources.yaml
4. custom-resources.yaml을 통해 custom-resource를 설치
kubectl create -f custom-resources.yaml
정상적으로 설치되었는지 확인
$ kubectl get pods --all-namespaces -w
calico-apiserver calico-apiserver-75757f57f7-8587h 0/1 Running 0 14s
calico-apiserver calico-apiserver-75757f57f7-g2vqj 0/1 Running 0 14s
calico-system calico-kube-controllers-64886d94fb-dxnf7 1/1 Running 0 85s
calico-system calico-node-ddgmw 1/1 Running 0 85s
calico-system calico-typha-7bf6c8fb7c-gnckv 1/1 Running 0 85s
calico-system csi-node-driver-zzdss 2/2 Running 0 85s
kube-system coredns-5d78c9869d-lkb9l 1/1 Running 0 11m
kube-system coredns-5d78c9869d-qcbnv 1/1 Running 0 11m
kube-system etcd-k8s-master 1/1 Running 0 11m
kube-system kube-apiserver-k8s-master 1/1 Running 0 11m
kube-system kube-controller-manager-k8s-master 1/1 Running 0 11m
kube-system kube-proxy-rbzkh 1/1 Running 0 11m
kube-system kube-scheduler-k8s-master 1/1 Running 0 11m
tigera-operator tigera-operator-5f4668786-2qtbj 1/1 Running 0 114s
12. 마스터 격리 해제
- 선택 사항
- master node 에 pod 가 설치가 가능하도록 격리를 해제한다
Worker Node
없이Master Node
로만 클러스터를 구축하는 경우 해당 명령어 사용하도록 함
마스터 노드 격리 해제
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
격리 해제 확인
# STATUS가 Ready로 바뀌어야 함.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-node Ready control-plane 19m v1.27.6
13. 대쉬보드 설치
1. Dashboard 설치
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/dashboard-2.7.0/dashboard.yaml
2. Metrics Server 설치
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/metrics-server-0.6.3/metrics-server.yaml
‣ 워커 노드
- 워커 노드에서 14~15번 작업 진행
14. 워커노드 등록
master-node 설치에서 진행했던 마스터 노드 초기화의 5번 순서에서 얻은 토큰 값을 참고하여 워커 노드 서버에 토큰을 등록
# 워커 노드가 될 서버에서 실행
sudo kubeadm join 192.168.121.56:6443 --token o99wfs.****** \
--discovery-token-ca-cert-hash sha256:*****
💥 토큰 값을 까먹었거나 파일이 삭제됐어요!
마스터 노드에서 다음과 같이 토큰을 다시 얻을 수 있다.
kubeadm token create --print-join-command
15. 워커노드 확인
Master node 서버로 이동해 아래 명령어 실행
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master-node Ready control-plane 29m v1.27.6 192.168.121.56 <none> Ubuntu 20.04.6 LTS 5.15.0-84-generic containerd://1.6.24
worker-node1 NotReady <none> 19s v1.27.6 192.168.121.101 <none> Ubuntu 22.04.3 LTS 6.2.0-33-generic containerd://1.6.24
sub 라는 NAME의 worker node가 설치된 것을 확인할 수 있다.
▶︎ Clean UP
- 쿠버네티스 삭제
- 워커노드, 마스터노드 전부 진행
쿠버네티스 삭제
sudo kubeadm reset
sudo apt-get -y purge kubeadm kubectl kubelet kubernetes-cni kube*
sudo apt-get -y autoremove
sudo rm -rf ~/.kube
잔여 CNI 캐시 삭제
sudo rm /etc/cni/net.d
# clean up iptables rules or IPVS tables.
sudo apt install ipvsadm
sudo ipvsadm --clear
▶︎ 셸 스크립트로 한방에!
위 모든 과정을 쉘 스크립트로 작성하여 자동화해보자.
참고로 해당 스크립트들은 root 권한/계정에서 실행하도록 한다.
1. 모든 노드에서 실행하는 스크립트
install_default.sh
#!/usr/bin/bash
#=========================================#
# All Node Script #
#=========================================#
echo "============== [1] 마스터 노드 & 워커 노드 기본 설정 =============="
echo -e "====== [1-1] 패키지 최신화 ======\n"
sudo apt-get update -y && sudo apt-get upgrade -y
echo -e "====== [1-2] 타임존 설정 ======\n"
sudo timedatectl set-timezone Asia/Seoul
echo -e "====== [1-3] hosts 설정 ======\n"
sudo bash -c "echo '192.168.219.132 k8s-node1\n192.168.219.142 k8s-node2' >> /etc/hosts"
echo -e "====== [1-4] 스왑 비활성화 ======\n"
sudo swapoff -a && sudo sed -i '/ swap / s/^/#/' /etc/fstab
echo -e "====== [1-5] SELinux 비활성화 ======\n"
echo "" # pass
echo -e "====== [1-6] iptable 설정 ======\n"
cat << EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
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
sudo sysctl --system
echo -e "====== [1-7] k8s 도구 설치 ======\n"
sudo apt -y install curl apt-transport-https net-tools
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg |sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/kubernetes.gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.27/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.27/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt-get -y install kubeadm=1.27.13-2.1 kubelet=1.27.13-2.1 kubectl=1.27.13-2.1 --allow-downgrades
sudo apt-get update
sudo apt-mark hold kubelet kubeadm kubectl
echo -e "====== [1-8] CRI (ContainerD) 설치 ======\n"
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker-archive-keyring.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update -y
sudo apt-get install -y containerd.io-1.6.21-3.1.el8
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
echo -e "====== [1-8-1] CRI 활성화 ======\n"
sudo bash -c "containerd config default > /etc/containerd/config.toml"
sudo sed -i 's/ SystemdCgroup = false/ SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
echo "============== [1] 마스터 노드 & 워커 노드 기본 설정 완료! =============="
2. 마스터 노드에서만 실행하는 스크립트
master-node.sh
#!/usr/bin/bash
#=========================================#
# Master Node Script #
#=========================================#
echo "============== [2] 마스터 노드 설정 =============="
echo -e "====== [2-9] 마스터 클러스터 초기화 ======\n"
sudo kubeadm config images pull --cri-socket unix:///run/containerd/containerd.sock
sudo kubeadm init --pod-network-cidr=172.24.0.0/24 --apiserver-advertise-address 192.168.219.110
sudo kubeadm token create --print-join-command > ~/join.sh
echo -e "====== [2-10] kubectl 설정 ======\n"
USER_HOME="/home/$(cat /etc/passwd | grep '1000' | cut -d ':' -f 1)"
mkdir -p "${USER_HOME}/.kube"
sudo cp -i /etc/kubernetes/admin.conf $USER_HOME/.kube/config
sudo chown $(id -u):$(id -g) $USER_HOME/.kube/config
echo -e "====== [2-11] Calico 설치 ======\n"
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/tigera-operator.yaml
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/custom-resources.yaml
kubectl create -f tigera-operator.yaml
sed -ie 's/192.168.0.0\/16/172.24.0.0\/24/g' custom-resources.yaml
kubectl create -f custom-resources.yaml
echo -e "====== [2-13] 대쉬보드 설치 ======\n"
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/dashboard-2.7.0/dashboard.yaml
echo -e "====== [2-13-1] 매트릭스 설치 ======\n"
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/metrics-server-0.6.3/metrics-server.yaml
echo "============== [2] 마스터 노드 설정 완료!=============="
3. 모든 스크립트가 정상적으로 설치되었다면 워커 노드로 이동하여 worker-node 등록해주도록 하자.