최종 프로젝트 인프라 구축⑤-쿠버 갈아엎기
kubeadm 기반으로 클라스터 재구축
개요
minikube로 VM 외부에서 접근이 안되는 이유를 찾아봤더니..
미니큐브의 작동 원리가 쿠버 컨테이너를 만들어서 그 안에서 동작하는 것이었다.
그래서 그냥 쿠버를 네이티브로 설치하기로 했다.
일단 기존 VM을 복제해서 마스터와 슬레이브 노드 한 개씩 만들어 주었다.
당연히 마스터 노드의 미니큐브는 지웠고
마스터의 IP는 192.168.56.100, 슬레이브는 192.168.56.101 이다.
젠킨스 IP를 192.168.56.200으로 바꿔 주고…
/etc/hosts
파일에 각각 서버의 도메인 주소를 넣어 준다.
마스터, 슬레이브 노드 모두
아래의 작업은 마스터, 슬레이브 노드 모두 적용한다.
스왑 비활성화
우선 쿠버는 swap(가상 메모리)와 궁합이 맞지 않는다. swap을 비활성화해 주자.
1
2
3
4
# 즉시 비활성화
sudo swapoff -a
# 재부팅시에도 스왑 활성화되지 않도록
sudo sed -i.bak '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
SELinux 비활성화
SELinux도 비활성화(정확히는 보안 위반 시도를 로깅하는 Permissive모드)해야 쿠버노드와 호스트 간의 충돌을 피할 수 있다.
1
2
3
4
# 즉시 비활성화
sudo setenforce 0
# 재부팅시에도 비활성화 하도록
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
모듈 활성화
리눅스 커널의 overlay 모듈과 br_netfilter 모듈은 활성화해 준다.
overlay 모듈은 여러 파일 시스템을 통합 관리하여 컨테이너 관리에 사용되고, br_netfilter 모듈은 네트워크 트래픽 관리 역할로 포드 간 통신에 사용된다 한다.
1
2
3
4
5
6
7
8
9
# 즉시 활성화
sudo modprobe overlay
sudo modprobe br_netfilter
# 재부팅시에도
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
IP 포워딩
쿠버네티스의 방화벽 설정을 아래와 같이 해 준다.
쿠버의 IPv4/v6 통신이 iptables 방화벽으로 처리되도록 하고(net.bridge.bridge-nf-call)
IPv4 포워딩을 활성화하여 노드 간, 외부와의 통신도 가능하도록 한다(net.ipv4.ip_forward)
1
2
3
4
5
6
7
8
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
# 파라메터 즉시 적용
sudo sysctl --system
네트워크매니저 설정
나중에 쿠버 설치하고 할 CNI(Calico) 설정 때문이지만,
NetworkManager 설정을 다음과 같이 해 준다.
(CNI는 간단하게 컨테이너 네트워크 관리자라 보면 된다)
1
2
3
4
5
6
7
sudo tee /etc/NetworkManager/conf.d/calico.conf << EOF
[keyfile]
unmanaged-devices=interface-name:cali*;interface-name:tunl*;interface-name:vxlan.calico;interface-name:vxlan-v6.calico;interface-name:wireguard.cali;interface-name:wg-v6.cali
EOF
# 재시작
sudo systemctl restart NetworkManager
위 설정이 무슨 의미냐면 NetworkManager가 Calico CNI를 제어하지 않도록 해 서로 충돌나지 않게 해 주는 것이다.
방화벽
firewalld
방화벽은 꺼 준다.
1
2
sudo systemctl stop firewalld
sudo systemctl disable firewalld
컨테이너 런타임 설치
여기서 컨테이너 런타임이란, 쿠버네티스가 사용할 컨테이너 엔진을 말하는 것이다.
보통은 containerd
런타임을 사용하지만, 이 서버는 우분투가 아니기에 레드헷 계열에 최적화된 CRI-O 설치를 하게 되겠다.
여기서 잠깐, docker와 podman? containerd와 cri-o?
일단 컨테이너 기술
이라는 것은 잘 알 테고…
쉽게 설명하자면 컨테이너 기술을 구현하기 위한 최소한의 환경이 컨테이너 런타임이다.
대표적으로 containerd가 있는데, docker에서 컨테이너 기술만 따로 떼어낸 것이라고..
cri-o는 레드햇에서 만든 컨테이너 런타임이다. 범용적인 containerd와 달리
그걸 확장시켜 컨테이너를 빌드하고, 관리하는 등 종합적으로 만든 것이 컨테이너 플랫폼이다.
대표적으로 docker와 podman이 있는데, docker의 단점(컨테이너 데몬?)을 보완한 것이 podman이다.
docker와 podman은 99.999% 호환된다 보면 되고, 레드햇 계열에서는 podman을 더 권장한다 한다.
즉 컨테이너 런타임(containerd,cri-o)⊂컨테이너 플랫폼(docker,podman) 이라는 것이다.
다시
참고로 운영체제가 록키 8에 커널 버젼이 4.18이기에, 1.28 버전을 설치하기로 했다.
그 이상 버젼은 커널이나 systemd(OS 시스템) 둘 중 하나가 지원하지 않기 때문이다…
(괜히 최신버젼 쓰겠다고 삽질하다 하루 날려먹고 얻은 결론)
어쨌든 cri-o를 설치할 레포지토리를 깔아 주고 설치한다.
1
2
3
4
5
6
7
8
9
10
11
# crio레포 추가, 참고로 dnf도 레포는 /etc/yum.repos.d에서 관리합니다~
cat <<EOF | sudo tee /etc/yum.repos.d/cri-o.repo
[cri-o]
name=CRI-O
baseurl=https://pkgs.k8s.io/addons:/cri-o:/stable:/v1.28/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/addons:/cri-o:/stable:/v1.28/rpm/repodata/repomd.xml.key
EOF
sudo dnf install -y cri-o container-selinux
cri-o를 crictl 명령어로 관장하는 cri-tools
는 위 레포가 먹지 않아, 수동 설치해줘야 한다.
1
2
3
4
5
6
7
8
9
10
11
# 받아다가
wget "https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.28.0/crictl-v1.28.0-linux-amd64.tar.gz"
# 압축 풀고
tar zxvf "crictl-${VERSION}-linux-${ARCH}.tar.gz"
# 실행환경으로 옮기기
sudo mv crictl /usr/bin
# 지금 crio 활성화하고, 재부팅시에도 자동으로
sudo systemctl enable --now crio
쿠버네티스 설치
컨테이너 런타임까지 완료 되었으면, 드디어 쿠버를 설치할 환경이 구축되었다.
서버가 당신을 기다린다. 빨리 설치해 주자.
1
2
3
4
5
6
7
8
9
10
11
12
13
# 쿠버레포 추가
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
# dnf로 설치,
sudo dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
레포파일에 exclude
를 설정한 이유는 다른 레포지토리와의 버전 충돌을 막기 위해 적힌 항목는 이 레포에서 설치 안한다이며,
dnf 설치시에는 --disableexcludes
명령어를 통해 일시적으로 exclude를 무시하여 위 레포로 우선 설치되도록 해 주었다.
저렇게 복잡하게 한 큰 이유는 없고, 1.28 버전을 깔아야 해서 그랬다나~
되었으면 kubelet이 부팅시 자동 실행되도록 해 주되, --now
옵션은 제외한다.
아직 클라스터 구축이 되지 않아 지금 실행해도 kubelet이 오류를 뿜기 때문이다.
1
sudo systemctl enable kubelet
쿠버 클라스터 실행-마스타노드
본 작업은 마스터 노드에서만 수행해야 한다.
1
sudo kubeadm init --pod-network-cidr=10.1.0.0/16 --cri-socket=unix:///var/run/crio/crio.sock --apiserver-advertise-address=(마스터노드 ip)
이렇게 kubeadm init 명령어로 클라스타를 실행시킬 수 있다.
--pod-network-cidr
은 포드에 할당할 아이피 대역을(VM IP 대역과는 가급적 겹치지 않기를)--cri-socket
은 cri-o 연결을--api-advertise-address
은 (마스터 노드가) 슬레이브 노드에게 알릴 자신의 주소
를 의미한다.
그렇게 하다 보면.. 뭔가가 주르륵 내려 가다…
Your Kubernetes control-plane has initialized successfully!
이게 뜨면 성공이다!
여기서 중요한 것이 successfully 아래의
1
2
3
4
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.56.100:6443 --token e124eg.xsn2jba5r03rwa4w \
--discovery-token-ca-cert-hash sha256:2043a75fb28dc1c703272777e8d18466bf26f3ffa8f33b9f283148eb43cb805e
여기의 kubeadm join 부분을 반드시 백업해 두자!
(사족: 아까 말한 쿠버 버전이랑 커널 버전 관련 에라가 여기서 발생했는데,
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
이 다음을 나아가지 못했다.
핵심은 커널을 업데이트 해도 OS 시스템(systemd)가 구버전이라 kubeadm이 cpu 가상화를 받아들일 수 없었던 것)
그 다음, kubectl 권한 부여를 위해
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
이렇게 설정해 준다.
(루트 상태이면 export KUBECONFIG=/etc/kubernetes/admin.conf
이렇게만 해줘도 끝)
마지막으로, 컨테이너 네트워크 인터페이스(CNI)를 설치해야 하는데
이건 DNS 제공, 외부 통신 등 클라스터 내부 네트워크 제어 및 외부와의 연결을 위해 필요하다.
(없어도 쿠버 기본 CNI가 해준다는데 일단 이게 뭔지 알았으니까)
이것도 calico랑 flannel 두 가지가 있는데 calico의 기능이 더 다양하다.
(calico는 L3-IP, flannel은 L2-MAC 단계에서 작동한다 하네요)
1
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
그냥 이렇게 해 줘도 되지만, 이러면 전부 기본 설정으로 되기 때문에 커스텀 설치를 해 주자.
1
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yaml
tigera-operator
를 깔면 calico 커스텀 설정이 가능하다.
커스텀 설정은
1
curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/custom-resources.yaml
이렇게 받을 수 있고, 받은 custom-resources.yaml
을 수정해 주자.
들어가 보면
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
# This section includes base Calico installation configuration.
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.Installation
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configures Calico networking.
calicoNetwork:
ipPools:
- name: default-ipv4-ippool
blockSize: 26
cidr: 192.168.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
---
# This section configures the Calico API server.
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.APIServer
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
name: default
spec: {}
이렇게 되어 있을 텐데 spec: calicoNetwork: ipPools:
에 있는 cidr이 중요하다.
이걸 아까 kubeadm init
할 때 설정했던 --pod-network-cidr
값과 동일하게 해 준다.
저장하고 kubectl apply -f custom-resources.yaml
을 해 주면…
이렇게 calico-system 네임스페이스에 calico-… 포드들이 생긴 것을 볼 수 있다!
주의할 점은 저 calico-node-…가 전부 running 상태가 되어야 한다는 것이다.
노드가 추가될 때마다 calico-node도 하나씩 추가되는데, 하나라도 Running이 아니면 클라스터가 그 노드와 통신을 못 하기 때문이다.
클라스터 연결-슬레이브노드
이번에는 슬레이브 노드로 돌아와서
아까 kubeadm init
할 때 나왔던 kubeadm join … 명령어를 실행시켜 준다.
그러면 마스터 때와 마찬가지로 뭔가 주르륵 흘러가고…
This node has joined the cluster
이게 뜨면 성공이다!
마스타 노드에서 kubectl get nodes
로 확인해 보면…
이렇게 슬레이브 노드가 추가된 것을 볼 수 있다!
kubectl get pods -n calico-system
으로 calico-node가 추가되었고, running 상태인지도 봐 주자.
다행히 calico-node 2개 모두 running인 것을 볼 수 있다!
(running 안 되는 오류 때문에 다시 설치해서 노드명이 위와 달라요)
클라스터 초기화
본인이 겪었던 calico-node 안올라오는 오류 등으로 클라스터를 갈아엎고 다시 만들고 싶을 경우,
다음과 같이 해 준다.
우선 슬레이브 노드부터(순서 중요!)
1
2
3
4
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d
sudo rm -rf /etc/kubernetes/*
그 다음 모든 슬레이브 노드에서 위 짓을 하고 마스타 노드에서
1
2
3
4
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d
sudo rm -rf $HOME/.kube/config
kubeadm reset을 할 때 대놓고 /etc/cni/net.d
(CNI 설정)랑 $HOME/.kube/config
(클라스터 설정) 남아 있으니 지우라고 한다.
슬레이브 노드도 /etc/kubernetes/
여기를 지워 줘야 나중에 재구성할때 kubeadm join을 원활할게 할 수 있다.
마치며
이렇게 환경을 구성하고 젠킨스파일을 살~짝 수정해 빌드해 봤지만
ErrImgNotPull
에라가 났었다.
즉 이미지를 레지스트리에 올리지 않았기 때문에 쿠버가 이미지를 읽지 못한다는 것인데…
어차피 레지스트리를 만들 겸, 이 참에 helm
과 argocd
도 사용해 완벽한 로컬 배포 환경을 만들어 봐야겠다.
아무튼 그때까지 안녕~