[NCP] 네이버 클라우드에 k8s HA(고가용성) 클러스터 구축하기
📌Index
- 프로젝트 목표
- VPC 및 Subnet 생성하기
- Init Script 작성하기
- Server 생성하기
- Server 세팅하기
- Kubernetes 초기화 및 노드 조인
- CNI(Container Network Interface) 설치
- 마무리
✔️ 프로젝트 목표
NCP(네이버 클라우드)에서 고가용성(HA) 쿠버네티스 토플로지 : stacked etcd
를 구성한다.
stacked etcd(중첩된 토플로지)는 etcd에서 제공하는 분산 데이터 스토리지 클러스터를, 컨트롤 플레인 구성 요소를 실행하는 kubeadm
으로 관리되는 노드에 의해서 형성된 클러스터 상단에 중첩하는 토플로지이다.
고가용성 쿠버네티스 토플로지에 대한 더 자세한 설명은 다음에서 확인할 수 있다.
NCP 서비스 | 설명 |
---|---|
VPC | 격리된 가상 네트워크 구성 |
Server | lb node, master node, worker node 구성 |
노드 | 개수 | OS |
---|---|---|
로드 밸런서 노드 | 1개 | ubuntu-20.04 |
마스터 노드 | 3개 | ubuntu-20.04 |
워커 노드 | 3개 | ubuntu-20.04 |
✔️ VPC 및 Subnet 생성하기
가장 먼저 VPC와 서브넷을 다음과 같이 생성한다.
항목 | CIDR |
---|---|
VPC | 172.16.0.0/16 |
Public Subnet | 172.16.0.0/20 |
생성 과정 및 방법은 다음에서 확인할 수 있다.
✔️ Init Script 작성하기
Swapoff, docker 설치 및 kubernetes 설치를 7개의 Server에서 모두 진행하는 것이 상당히 번거롭기 때문에, NCP의 Init Script를 작성해서 Server 생성 시 사용한다.
init script는 [Server] - [Init Script] - [Script 생성]을 통해 등록할 수 있다.
🔧Trouble Shooting
초기에 다음과 같이 init sript를 작성했으나, 접속하여 확인했을 때 docker만 설치되어있을 뿐 kubectl, kubelet, kubeadm은 설치되어있지 않았다.
#!/bin/bash
#------Swap Disable-----#
echo "Swap off"
sudo swapoff -a
echo 0 > /proc/sys/vm/swappiness
sed -e '/swap/ s/^#*/#/' -i /etc/fstab
#------Install Docker-----#
echo "Install docker"
sudo apt update
sudo apt install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker
cat << EOF | sudo tee –a /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo systemctl daemon-reload
sudo systemctl restart docker
#------Install k8s-----#
echo "Install kubernetes"
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/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
따라서 로그 파일을 확인해보았다.
ncp의 init script의 로그는 /var/log/ncloud-init.log
에 저장된다.
🔎 참고
서버의 상태가 운영중으로 표시되더라도 스크립트 설치가 계속 진행 중일 수도 있다. 설치 완료 여부를 확인하려면 진행 상태 로그를 확인한다. 관련 사항은 ncloud-docs에서 확인할 수 있다.
- Linux:
/var/log/ncloud-init.log
- Windows:
C:\Program Files(X86)\NBP\ncloud-init\logs
cat /var/log/ncloud-init.log
명령으로 로그를 확인해보니, 주요 원인은 다음과 같았다.
curl: (7) Failed to connect to packages.cloud.google.com port 443: Connection timed out
kubernetes 설치 과정 중 gpg 키를 curl 명령어로 받을 때 발생하는 에러였다.
구글링해보니 방화벽 혹은 포트 문제일 것이라고 하지만, 방화벽도 해제되어있고 포트도 문제 없이 열려있었다. Server에 접속해서 동일한 명령어를 실행할 때는 에러 없이 잘 실행되는데, init script로 실행되면 에러가 발생하는 것이 의아하다.
결국 curl을 wget명령어로 대체함으로써 해결할 수 있었다. curl에 대한 문제는 더 생각해봐야 할 것 같다.
최종적으로 완성된 init script는 다음과 같다.
#!/bin/bash
#------Swap Disable-----#
echo "Swap off"
swapoff -a
echo 0 > /proc/sys/vm/swappiness
sed -e '/swap/ s/^#*/#/' -i /etc/fstab
#------Install Docker-----#
echo "Install docker"
sudo apt update
sudo apt install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker
cat << EOF | sudo tee -a /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo systemctl daemon-reload
sudo systemctl restart docker
#------Install k8s-----#
echo "Install kubernetes"
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
echo "Gpg Key......."
wget https://packages.cloud.google.com/apt/doc/apt-key.gpg
mv apt-key.gpg /usr/share/keyrings/kubernetes-archive-keyring.gpg
echo "Repository......."
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
echo "Update....."
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
이제 이 script를 Server 생성 시 사용하면, docker 및 kubernetes 설치가 완료된 상태에서 시작할 수 있다.
✔️ ACG 생성하기
ACG란 네이버 클라우드의 IP 주소/포트 기반 필터링 기능으로, AWS의 보안 그룹과 같다고 생각하면 된다.
쿠버네티스 공식 문서를 보면, 허용해줘야 하는 포트 번호 및 프로토콜을 확인할 수 있다.
컨트롤 플레인(마스터 노드)
프로토콜 | 방향 | 포트 범위 | 용도 | 사용 주체 |
---|---|---|---|---|
TCP | 인바운드 | 6443 | 쿠버네티스 API 서버 | 전부 |
TCP | 인바운드 | 2379-2380 | etcd 서버 클라이언트 API | kube-apiserver, etcd |
TCP | 인바운드 | 10250 | Kubelet API | Self, 컨트롤 플레인 |
TCP | 인바운드 | 10259 | kube-scheduler | Self |
TCP | 인바운드 | 10257 | kube-controller-manager | Self |
워커 노드
프로토콜 | 방향 | 포트 범위 | 용도 | 사용 주체 |
---|---|---|---|---|
TCP | 인바운드 | 10250 | Kubelet API | Self, 컨트롤 플레인 |
TCP | 인바운드 | 30000-32767 | NodePort 서비스 | 전부 |
위를 참조하여 ACG를 생성하자.
master node의 ACG ny-acg-k8s-master
worker node의 ACG ny-acg-k8s-worker
아웃바운드는 둘 다 모든 포트(1-65535)를 열어주었다.
✔️ Server 생성하기
로드 밸런서 노드
가장 먼저 로드밸런서 노드로 사용할 서버를 만들자. 본 프로젝트에서는 Standard 타입의 ubuntu-20.04
이미지를 사용하였다.
VPC와 서브넷을 선택해준 후, 네트워크 인터페이스도 추가해준다.
이때 [새로운 공인 IP 할당] 옵션을 선택하면 자동으로 공인 IP가 할당되어 나중에 따로 공인 IP를 부여해주지 않아도 된다. 그리고 앞서 작성한 init script를 선택해준다.
서버의 인증키는 기존의 키가 있다면 선택하고, 없다면 새로 생성하여 다운 받는다.
ACG는 앞서 생성한 ny-acg-k8s-master
를 사용한다.
마스터 노드
마스터 노드의 기본 설정은 로드밸런서 노드와 동일하다. 개수만 3개로 하며, 서버를 한번에 2개 이상 생성할 때는 공인IP 자동할당이 불가능하여, 따로 부여해줘야한다. 마스터 노드의 설정은 다음과 같다.
워커 노드
워커 노드 또한 로드밸런서 노드와 기본 설정이 동일하고, 3개를 생성하며, ACG는 앞서 생성한 ny-acg-k8s-worker
를 선택한다.
시간이 지나 모두 서버 생성이 완료되면, 마스터 노드와 워커 노드에 모두 공인 IP를 부여해준다.
✔️ Server 세팅하기
로드밸런서 구성하기
로드밸런서 노드의 공인IP를 통해 SSH 접속하자.
ssh root@[로드밸런서 노드의 공인 IP]
접속 비밀 번호는 [서버 관리 및 설정 변경] - [관리자 비밀번호 확인]을 클릭하여, 앞서 서버의 인증키로 설정한 키를 사용하면 확인할 수 있다. 이 과정을 매번 반복하는 것이 귀찮다면 다음 명령어를 통해 직접 비밀번호를 설정할 수 있다.
passwd
고가용성(HA)을 위해 필요한 것이 로드밸런서이다. 로드 밸런서 뒤에 있는 apiserver 중 한개의 apiserver에 장애가 발생해도, 나머지 apiserver로 정상적인 서비스를 하도록 로드를 분배한다.
로드 밸런서는 HAproxy
라는 application을 설치한다.
sudo apt update && sudo apt install haproxy -y
다음으로 HAproxy
설정 파일을 다음과 같이 입력한다.
cat << EOF | sudo tee -a /etc/haproxy/haproxy.cfg
frontend kubernetes-master-lb
bind 0.0.0.0:6443
option tcplog
mode tcp
default_backend kubernetes-master-nodes
backend kubernetes-master-nodes
mode tcp
balance roundrobin
option tcp-check
option tcplog
server ny-k8s-master001 [마스터 노드 1의 private ip]:6443 check
server ny-k8s-master002 [마스터 노드 2의 private ip]:6443 check
server ny-k8s-master003 [마스터 노드 3의 private ip]:6443 check
EOF
다음으로 변경 사항이 적용될 수 있도록 HAproxy
시스템을 재시작한다.
sudo systemctl restart haproxy
sudo systemctl enable haproxy
마지막으로 SELinux
을 permissive mod(disable)로 세팅한다.
sudo apt install selinux-utils
setenforce 0
공통 구성
모든 노드에서 hosts 파일을 설정해줘야한다. 안해도 큰 문제는 없지만 ip 주소를 일일히 입력하는 대신 도메인 네임을 사용하는 것이 더 편리하다.
sudo vi /etc/hosts
hosts 파일에 아래 내용을 추가한다.
[로드밸런서 노드의 private ip] ny-k8s-lb
[마스터 노드 1의 private ip] ny-k8s-master001
[마스터 노드 2의 private ip] ny-k8s-master002
[마스터 노드 3의 private ip] ny-k8s-master003
[워커 노드 1의 private ip] ny-k8s-worker001
[워커 노드 2의 private ip] ny-k8s-worker002
[워커 노드 3의 private ip] ny-k8s-worker003
✔️ Kubernetes 초기화 및 노드 조인
Kubernetes 초기화
컨트롤 플레인 노드 중 하나(ny-k8s-master001)에서 kubeadm init
명령을 통해 kubernetes 초기화 작업을 진행한다.
sudo kubeadm init --control-plane-endpoint ny-k8s-lb:6443 --pod-network-cidr 172.16.0.0/16 --apiserver-advertise-address=0.0.0.0 --apiserver-cert-extra-sans=ny-k8s-master001,ny-k8s-master002,ny-k8s-master003 --upload-certs
--control-plane-endpoint
: 로드밸런서 서버와6443
포트를 입력--pod-network-cidr
: 쿠버네티스에서 사용할 컨테이너의 네트워크 대역을 지정--apiserver-advertise-address
: 쿠버네티스가 생성한 TLS 인증서에 적용할 IP 또는 도메인을 명시 => 현재 마스터 노드의 ip 주소 입력--upload-certs
: 노드 join에 필요한 cert key값을 받기 위해 필요--apiserver-cert-extra-sans
: API 서버 제공 인증서에 사용할 선택적 추가 SAN(주체 대체 이름), 마스터 노드의 ip를 세팅
성공하면 다음과 같은 결과가 출력된다.
- 작업하는 환경마다 token, hash, key 값은 다르다.
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/
You can now join any number of the control-plane node running the following command on each as root:
kubeadm join ny-k8s-lb:6443 --token 2swoy9.kimg4x50uhe57ony \
--discovery-token-ca-cert-hash sha256:98e5f153b72117c12cd86ad8eebc70e7a9cc9419ca5d36ae9d4516b21d5683a1 \
--control-plane --certificate-key bf2423da961b7d8b70bf97db73fc3cb36fe5eebb188c282b971d7f9857a9dd0d
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join ny-k8s-lb:6443 --token 2swoy9.kimg4x50uhe57ony \
--discovery-token-ca-cert-hash sha256:98e5f153b72117c12cd86ad8eebc70e7a9cc9419ca5d36ae9d4516b21d5683a1
kubectl
명령어 사용을 위해서는 인증 파일을 생성해야한다.
kubectl
은 항상 $HOME/.kube
를 참조한다. root가 아닌 노멀 사용자는 반드시 인증이 필요하다.
이 파일은 쿠버네티스에 인증하기 위한 파일로, 절대 노출되면 안된다.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
노드 조인
다른 마스터 노드에서 아래와 같은 명령을 실행하여 ny-k8s-master001
으로 조인한다. (앞서 컨트롤 플레인의 initialization 시 출력되는 값을 참조한다) 이때, 루트로 실행해야하기 때문에 sudo
를 붙이거나,
sudo -i
명령으로 루트 사용자로 전환한다.
sudo kubeadm join ny-k8s-lb:6443 --token 2swoy9.kimg4x50uhe57ony \
--discovery-token-ca-cert-hash sha256:98e5f153b72117c12cd86ad8eebc70e7a9cc9419ca5d36ae9d4516b21d5683a1 \
--control-plane --certificate-key bf2423da961b7d8b70bf97db73fc3cb36fe5eebb188c282b971d7f9857a9dd0d
워커 노드들에서는 컨트롤 플레인 노드로 조인하기 위해 다음 명령을 실행한다.
sudo kubeadm join ny-k8s-lb:6443 --token 2swoy9.kimg4x50uhe57ony \
--discovery-token-ca-cert-hash sha256:98e5f153b72117c12cd86ad8eebc70e7a9cc9419ca5d36ae9d4516b21d5683a1
모든 노드에서 조인이 성공하면 결과는 다음과 같다.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ny-k8s-master001 NotReady control-plane 12m v1.25.2
ny-k8s-master002 NotReady control-plane 9m53s v1.25.2
ny-k8s-master003 NotReady control-plane 8m17s v1.25.2
ny-k8s-worker001 NotReady <none> 7m47s v1.25.2
ny-k8s-worker002 NotReady <none> 6m33s v1.25.2
ny-k8s-worker003 NotReady <none> 5m34s v1.25.2
아직 상태가 NotReady
인 것을 확인할 수 있다.
kubeadm init
명령 시 add-on
을 완성하지 않았었기 때문에(CNI(container network interface) 설정), 구성을 마무리 해줘야한다. Cluster Networking | Kubernetes
✔️ CNI(Container Network Interface) 설치
여러가지 CNI 플러그인이 있지만 여기서는 Calico를 사용한다.
curl
명령어로 yaml
파일을 다운 받는다.
curl https://docs.projectcalico.org/manifests/calico-typha.yaml -o calico.yaml
calico.yaml
파일을 다음과 같이 수정한다. 주석 #
을 해제하고, kubeadm init
명령 시 설정했던 pod의 cidr와 동일하게 맞춰줘야한다.
vi calico.yaml
- name: CALICO_IPV4POOL_CIDR
value: "172.16.0.0/16"
다음으로 calico를 설치한다.
kubectl apply -f calico.yaml
시간이 조금 지나면 모두 Ready
상태가 되는 것을 확인할 수 있다.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ny-k8s-master001 Ready control-plane 17m v1.25.2
ny-k8s-master002 Ready control-plane 14m v1.25.2
ny-k8s-master003 Ready control-plane 13m v1.25.2
ny-k8s-worker001 Ready <none> 12m v1.25.2
ny-k8s-worker002 Ready <none> 11m v1.25.2
ny-k8s-worker003 Ready <none> 10m v1.25.2
이때, pod들도 모두 Running
상태가 되어야한다.
$ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-58dbc876ff-868n6 0/1 Running 0 64s
kube-system calico-node-5kwf2 0/1 Running 0 64s
kube-system calico-node-d5z46 0/1 Running 0 64s
kube-system calico-node-jc2f9 0/1 Running 0 64s
kube-system calico-node-jzc24 0/1 Running 0 64s
kube-system calico-node-zdn9z 0/1 Running 0 64s
kube-system calico-node-zv79m 0/1 Running 0 64s
kube-system calico-typha-644446b795-b95tt 1/1 Running 0 64s
kube-system coredns-565d847f94-h5lm4 0/1 Running 0 17m
kube-system coredns-565d847f94-vgpr9 0/1 Running 0 17m
kube-system etcd-ny-k8s-master001 1/1 Running 2 18m
kube-system etcd-ny-k8s-master002 1/1 Running 0 15m
kube-system etcd-ny-k8s-master003 1/1 Running 0 13m
kube-system kube-apiserver-ny-k8s-master001 1/1 Running 2 18m
kube-system kube-apiserver-ny-k8s-master002 1/1 Running 0 15m
...
watch
명령어로 실시간으로 확인해볼 수 있고, 시간이 조금 지나면 모두 Running
상태가 되는 것을 확인할 수 있다.
watch kubectl get pods -A
✔️ 마무리
지금까지 네이버 클라우드에서 고가용성 쿠버네티스 중첩된 토플로지를 구성해보았다. 이전에 쿠버네티스 클러스터를 구축해본 적은 있으나, 여러개의 마스터 노드를 두고, 로드 밸런서 노드를 활용하는 것은 처음이었다. init script 작성 과정 및 노드 조인 과정에서 여러가지 에러를 겪었지만, 그 과정에서 쿠버네티스에 대한 이해도를 더 높일 수 있었던 것 같다. 다음에는 간단한 애플리케이션을 사용하여 컨트롤러를 생성한 뒤, 모니터링 서비스 prometheus, grafana를 구축하여 부하 테스트까지 진행해볼 에정이다.
네이버 클라우드는 본 프로젝트를 통해 처음 접하게 되었다. 아직 VPC, Server 밖에 사용해보지 못했지만, 다음 기회에 NKS도 사용해 볼 수 있으면 좋을 것 같다. 사용하면서 크게 불편함은 느낄 수 없었고, 서버 생성 시 매번 입력/복붙 해야하는 user data를 사용하는 AWS와 달리, init script를 따로 생성해 둘 수 있어서 편리했다. 단, 타 퍼블릭 클라우드 AWS, Azure 등에 비해 서버 부팅 시간이 조금 느린 것이 조금 아쉬웠던 것 같다. 전체적으로는 만족하면서 사용하였다.