이전 글에 이어서 Vagrant로 프로비저닝된 Ubuntu 20.04 가상 머신들을 통해서 쿠버네티스 클러스터를 구축하는 과정을 공유한다.
기존 Vagrantfile 또한 활용하여 가상머신에서 할 일을 최소하하여 클러스터를 구축하는 것이 목적이다.
# 쿠버네티스 클러스터 구축 방법
우선 쿠버네티스 클러스터를 구축하기 위해 필요한 요소들과 방법들을 간략하게 정리한다.
쿠버네티스 클러스터 구성 요소
쿠버네티스는 컨테이너 오케스트레이션 자동화 도구로써 각 클러스터를 관리하는 마스터 노드(컨트롤 플레인)와 컨테이너화된 애플리케이션을 실행하는 워커 노드(데이터 플레인로 구성된다.
따라서, 컨트롤 플레인에 필요한 컴포넌트와 워커 노드에 필요한 컴포넌트가 다르다.
컨트롤 플레인 컴포넌트 - Worker Node
kube-apiserver: 쿠버네티스 API를 노출하는 컴포넌트이다. 실제 통신을 담당하는 서버이다. 이는 수평적으로 확장할 수 있게 설계되어 추가적인 인스턴스를 배포하여 확장이 가능하다.
etcd(엣씨디): 모든 클러스터 데이터를 담은 저장소로 사용되는 일관성 & 고가용성을 보장하는 Key Value 구조의 데이터베이스이다. etcd를 백업하고 있으면 클러스터를 복수할 수 있다.
kube-scheduler: 사용자가 요청한 컨테이너 애플리케이션을 어느 노드로 배치할 지 선택하는 컴포넌트이다.
kube-controller-manager: 컨트롤러들의 프로세스를 실행하는 컴포넌트이다. 노드, 잡, 엔드포인트슬라이스, 서비스어카운트 컨트롤러를 포함한다.
데이터 플레인 컴포넌트 - Worker Node
kubelet: 파드에서 컨테이너가 확실하게 동작하도록 관리하는 컴포넌트이다.
kube-proxy: 네트워크 프록시로, 쿠버네티스 서비스 개념의 구현체이다. 워커 노드의 통신을 담당하는 컴퓨넌트이다.
컨테이너 런타임: 컨테이너의 실행을 담당하는 소프트웨어이다.
애드온 - Addon
DNS: 쿠버네티스 서비스를 위해 DNS 레코드를 제공해주는 DNS 서버이다. 이를 구성하면 쿠버네티스에 의해 구동되는 컨테이너는 DNS Search에서 해당 DNS 서버를 자동으로 포함한다.
컨테이너 네트워크: 워커 노드들에 동작되는 컨테이너들 사이의 통신을 가능하게 하는 컴포넌트이다. 노드들 사이에 오버레이 네트워크를 구성하기 위해 필요하다.
클러스터 구축 방법
쿠버네티스는 마스터 노드와 워커 노드로 구성되어야 하므로 앞서 살펴본 컴포넌드들을 역할에 맞게 구성해야한다.
각 컴포넌트들을 직접 구성하여 클러스터를 구축하는 Hardway 방식을 사용할 수도 있고, 다른 도구를 사용하여 쿠버네티스 클러스터를 구축할 수도 있다.
Kubespray, KOps, ClusterAPI 등의 별도 도구를 사용하는 방법도 존재하고, 공식적으로 지원하는 방법인 kubeadm을 통해서 클러스터를 생성할 수 있다..
이번 클러스터 구축에서는 kubeadm을 통해 간단하게 구축할 계획이다.
나머지 상세 요소들은 아래를 따른다.
- version: 1.28
- CRI: containerd
- CNI:
FlannelCalico (외부 노출을 위해 L3 인터페이스 사용)
원래 Flannel로 컨테이너 오버레이 네트워크를 구성했는데, 외부 노출하는 방식이 어려워서 L3 방식을 지원하는 Calico를 사용하여 클러스터를 구성하였다.
# 클러스터 구축하기
Vagrantfile에서 스크립트를 정의한 후 프로비저닝 과정에서 동작시켜 가상의 관리자의 개입을 최소화하도록 구성한다.
사전 작업 with Vagrantfile
Vagrant를 사용하여 사전 작업을 스크립트에 담아 실행한다.
echo 'root:test1234' | sudo chpasswd # root 계정 passwd 지정
sudo echo "192.168.1.100 master" >> /etc/hosts # master ip 호스트네임 매핑
sudo echo "192.168.1.101 worker1" >> /etc/hosts # worker1 ip 호스트네임 매핑
sudo echo "192.168.1.102 worker2" >> /etc/hosts # worker2 ip 호스트네임 매핑
sudo sysctl net.ipv4.ip_forward=1 # iptables 및 IP 포워딩 활성화
sudo modprobe br_netfilter # 브릿지 커널 모듈 로드
sudo swapoff -a # Swap 메모리 비활성화
# Containerd 설치
sudo apt-get update # 패키지 목록 업데이트
sudo apt-get install ca-certificates curl gnupg -y # 필요 패키지 설치
sudo install -m 0755 -d /etc/apt/keyrings # GPG 공개 키를 저장하기 위한 keyrings 디렉토리 생성 및 권한 부
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # Docker 공개 키 다운로드 및 .gpg 파일로 변환
sudo chmod a+r /etc/apt/keyrings/docker.gpg # 읽기 권한 부여
sudo echo \ # Docker 저장소를 패키지 목록에 등록
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update # 패키지 목록 업데이트
sudo apt-get install containerd.io # containerd 설치
sudo echo "" > /etc/containerd/config.toml # 설정 파일 삭제
sudo systemctl restart containerd # 재실행
여기에서 `/etc/containerd/config.toml` 파일을 삭제하는 이유는 기본적으로 containerd 설정이 CRI를 사용하지 않도록 되어 있기 때문이다.
# kubectl, kubeadm, kubelet 설치
sudo apt-get install -y apt-transport-https
sudo curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | \ # gpg 키 디코딩 후 저장
sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list # 쿠버네티스 저장소 추가
sudo apt-get update # 패키지 목록 갱신
sudo apt-get install -y kubelet kubeadm kubectl # kubelet, kubeadm, kubectl 최신 버전 다운로드
sudo apt-mark hold kubelet kubeadm kubectl # 자동 업그레이드 차단
마스터 노드에도 필요한 요소들을 동작시켜야하므로 kubelet 설치가 필요하다.
위 스크립트를 통해서 사전 준비를 수행할 수 있다.
# 마스터 노드용 스크립트
sudo kubeadm init --apiserver-advertise-address 192.168.1.100 2>&1 | tee /root/kubeadm_init_output.txt # 쿠버네티스 클러스터 초기화 - API 서버 주소 지정
sudo mkdir -p $HOME/.kube # kubernetes 설정 파일을 위한 디렉토리 생성
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config # 인증서 복사
sudo chown $(id -u):$(id -g) $HOME/.kube/config # 권한 부여
kubeadm을 통해서 클러스터 생성을 시작한다. 이를 별도의 파일로 전달하고, 추후에 활용한다.
위 스크립트를 Vagrantfile에 선언하여 프로비저닝 과정에서 수행한 후 아래의 명령어만 사용하면 쿠버네티스 클러스터 구축이 완료된다.
ssh root@worker1 "$(tail -n 2 kubeadm_init_output.txt)" # Worker Node 1 Cluster Join
ssh root@worker2 "$(tail -n 2 kubeadm_init_output.txt)" # Worker Node 2 Cluster Join
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml # CNI Calico 배포
# 클러스터 확인
kubeadm을 통해서 구축된 클러스터이다. 3개의 노드가 확인된다. 다만 STATUS가 NotReady 상태인 이유는 CNI를 구성하지 않았기 때문이다.
따라서 CNI로 사용할 Calico를 배포해주면 아래와 같이 상태가 정상적으로 찍힌다.
노드 상태를 자세히 확인해보면, 구성한 모든 내용이 정상적으로 잡힌다.
Calico를 통해 컨테이너의 네트워크를 구성했으므로, 해당 컴포넌트를 확인할 수 있고, 모든 Core Components가 정상적임을 알수 있다.
이제 간단한 애플리케이션을 배포하여 클러스터가 정상적으로 동작하는지 확인해보자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
spec:
replicas: 3
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- name: test
image: pengbai/docker-supermario:latest
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: test-service
spec:
selector:
app: test
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 8080
정상적으로 배포되었음을 확인할 수 있다.
NodePort로 노출시켜 이를 확인해보면 정상적으로 배포한 애플리케이션에 접근이 가능하다.
쿠버네티스 클러스터를 정상적으로 구축했다!
# 결론
아래는 이번 실습에서 구축한 클러스터를 시각화한 자료이다.
OS: Ubuntu 20.04.6 LTS
Kernel: 5.4.0-163-generic
K8s Version: 1.28.2
CRI: Containerd 1.6.25
CNI: Calico 3.26.1
Calico 이전 Flannel을 구성하다가, 안됐던 오류는 컨테이너 포트를 잘못 봐서 밀어버렸는데, 이 부분도 Vagrantfile이 남아있으니 다음에는 Flannel로 구성하여 시도해봐야겠다.
쿠버네티스의 각 컴포넌트에 대한 이해도를 더 올리기 위해서 kubeadm 없이 쿠버네티스 클러스터를 구축하는 방법과 나름의 Script를 작성해보는 것을 도전할 계획이다.
추가로, 네트워크에 대한 공부를 진행해야겠다. 배울건 참 많은 듯 하다.
이번 클러스터 구축에 사용한 Vagrantfile은 아래이다.
$pre_install = <<-SCRIPT
echo ">>>> pre-install <<<<<<"
echo 'root:test1234' | sudo chpasswd
sudo echo "192.168.1.100 master" >> /etc/hosts
sudo echo "192.168.1.101 worker1" >> /etc/hosts
sudo echo "192.168.1.102 worker2" >> /etc/hosts
sudo sysctl net.ipv4.ip_forward=1
sudo modprobe br_netfilter
sudo swapoff -a
echo ">>>> Install Containerd <<<<<<"
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg -y
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
sudo echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install containerd.io
sudo echo "" > /etc/containerd/config.toml
sudo systemctl restart containerd
echo ">>>> Install K8s Component <<<<<<"
sudo apt-get install -y apt-transport-https
sudo curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
SCRIPT
$mater_config = <<-SCRIPT
echo ">>>> Master Node Config <<<<<<"
sudo ssh-keyscan worker1 >> ~/.ssh/known_hosts
sudo ssh-keyscan worker2 >> ~/.ssh/known_hosts
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address 192.168.1.100 2>&1 | tee /root/kubeadm_init_output.txt
sudo 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
SCRIPT
Vagrant.configure("2") do |config|
# Master 노드 설정
config.vm.define "master" do |master|
master.vm.box = "generic/ubuntu2004"
master.vm.network "private_network", ip: "192.168.1.100"
master.vm.provider "vmware_desktop" do |v|
v.vmx['displayname'] = "Master"
v.gui = true
v.memory = 6144 # 6GB
v.cpus = 3
end
master.vm.hostname = "master"
master.vm.provision "shell", inline: $pre_install
master.vm.provision "shell", inline: $mater_config
end
# Worker 노드 설정
(1..2).each do |i|
config.vm.define "worker#{i}" do |worker|
worker.vm.box = "generic/ubuntu2004"
worker.vm.network "private_network", ip: "192.168.1.10#{i}"
worker.vm.provider "vmware_desktop" do |v|
v.vmx['displayname'] = "Worker#{i}"
v.gui = true
v.memory = 4096
v.cpus = 2
end
worker.vm.hostname = "worker#{i}"
worker.vm.provision "shell", inline: $pre_install
end
end
end
트러블 슈팅 및 참고 자료
GPG error: https://packages.cloud.google.com/apt kubernetes-xenial InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY B53DC80D13EDEF05
CRI v1 runtime API is not implemented for endpoint \"unix:///var/run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"
/proc/sys/net/bridge/bridge-nf-call-iptables does not exist
container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
> CNI 구성으로 해결
Error registering network: failed to acquire lease: node "worker1" pod cidr not assigned
Error registering network: failed to acquire lease: node "worker2" pod cidr not assigned
> Flannel 사용 시 Pod CIDR 지정 필요. `kubeadm init --pod-network-cidr=10.244.0.0/16`으로 해결
'Infra > Kubernetes' 카테고리의 다른 글
리스 - Lease (93) | 2023.12.21 |
---|---|
쿠버네티스 클러스터 구축하기 - 1 / 가상머신 세팅 - Vagrant & VMware Workstation 사용 (157) | 2023.12.03 |
노드 - Node (2) | 2023.11.22 |
Owners and Dependents & Recommended Labels - Pod & ReplicaSet & Deployment 관계 확인 (2) | 2023.11.04 |
Field Selector & Finalizers (2) | 2023.10.28 |