Kubernetes 기반 하이브리드(GCP+On-Premise) 클라우드 구축기
왜 우리는 쿠버네티스를 도입하는가?
Plaid는 DX,AX,R&D 의 주요 사업분야중 DX부문은 고객의 DX전환을 돕고 다양한 고객들의 요구사항을 충족하는 어플리케이션을 개발하고 이를 배포하고 관리하는 일을 담당합니다.
기존엔 어플리케이션의 배포를 어떤 플랫폼이든 원활히 배포하기 위해 Docker 이미지로 어플리케이션을 패키징 하는 것을 원칙으로 하였고, 이 이미지를 VM위에서 docker compose로 배포하였습니다.
기존의 CI/CD 구조
문제점
Horizontal Scailing을 하는 과정이 번거롭다.
서버마다 환경변수의 일관성을 유지하는 것이 어려움.
서비스의 수가 증가하며 컨테이너의 가동상태의 추적을 하는 것이 번거로워짐
롤백/장애복구가 쉽지 않음.
위와 같은 문제점을 개선할 수 있는 방안으로 레퍼런스가 많고 안정적인 Kubernetes를 도입하고자 하였습니다.
PLAID 맞춤 쿠버네티스 인프라의 구조
구글 클라우드에 GKE라고 하는 관리형 쿠버네티스 서비스를 제공해줍니다.
그런데 중요한 점은 Cost가 매우 비싸다는 것이고, GPU클러스터 노드를 추가하여 kubeflow ML Pipeline을 만드는 등 다양한 커스텀이 필요하다는 점에서 저희는 직접 kubeadm을 이용해 kubernetes Cluster를 구축하였습니다.
2대의 GCP VM 중 한대에는 Control Plane, 나머지 1대에는 WorkerNode로 구성하였습니다.
GCP VPC 외부에 존재하는 서버컴퓨터를 같은 클러스터에 묶기 위해 tailscale을 모든 노드에 설치하고 VPN망을 통해 원격의 컴퓨터를 하나의 클러스터로 묶으면서
GCP VM의 높은 비용 부담을 절감하며 동시에 외부 서버컴퓨터가 Down되는 상황이 생겨도 k8s의 자동 로드밸런싱과 스케일링 덕분에 가용성을 유지할 수 있게 되었습니다. 또한 GCP가 최근 AWS 중단 사태처럼 사용불능이 되더라도 plaid-gpu-1노드의 pod는 정상 작동 할 수 있습니다.
위와 같은 구조를 통해 Plaid는 클라우드 벤더 의존성을 줄이고 서비스 가용성을 향상시킬 수 있게 되었습니다.
Plaid의 쿠버네티스 플랫폼 컴포넌트
Plaid에서는 다양한 최신 쿠버네티스 플랫폼 컴포넌트를 사용중입니다.
네트워크 구현체 : Flannel
클러스터 인증 및 인가 : KEYCLOAK, Oauth2Proxy, Dex
MLOps: Kubeflow
서버리스: KNative
키 관리 서비스: Vault ,External Secret Operator
서비스 메시: istio (kubeflow 설치에 포함)
CI/CD: Jenkins , ARGOCD
StorageClass: LongHorn
사용성 간편화 도구: Helm
메트릭 파이프라인 : Prometheus, Grafana
로그파이프라인: elastic search(예정), kibana(예정), fluentbit(예정)
클러스터 프로비저너: kubeadm
기타 : Opentelemerty(예정),Cert-Manager, K-watch
* (예정) 컴포넌트들은 아직 설치되지 않은 컴포넌트→ 추후 설치 계획
Plaid의 Kubernetes 클러스터 세팅 과정
2대의 VM을 Google CloudPlatForm에 ubuntu22.04 x86 아키텍쳐를 기반으로 세팅하였습니다.
외부의 서버도 똑같은 운영체제와 같은 아키텍쳐를 사용하였습니다.
모든 노드에 tailscale을 설치하여 같은 VPN 망으로 묶었습니다.
2-1 : 워커 노드는 아래의 설치과정 진행 .
필수 패키지 설치
sudo apt-get install \\
ca-certificates \\
curl \\
gnupg \\
lsb-release
도커 GPG KEY 추가, 레파지토리 등록
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 docker version
sudo systemctl status docker
## 서버 재시작 시 docker 가 자동으로 시작되도록 설정 ##
sudo systemctl enable docker
노드간 통신을 위한 iptables에 브릿지 관련 설정 추가
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
swapoff 설정 (메모리 swap off)
swapoff -a
apt update 및 ca 관련 패키지 다운로드 및 설정
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
curl -s <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-apt-keyring.gpg] <https://pkgs.k8s.io/core:/stable:/v1.34/deb/> /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
7 . kubectl kubelet kubeadm 설치
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable --now kubelet
systemd , cgroup 설정 맞추기
sudo mkdir /etc/docker
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
재시작
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker
10 .마스터 노드에 join하기
kubeadm join <마스터노드IP>:6443 --token <토큰> \\
--discovery-token-ca-cert-hash <해쉬>
마스터 노드의 세팅 방법은 이 포스트에선 생략합니다.
설치과정 중 만난 오류들
kubeadm init중 발생하는 CRI 에러
[preflight] Running pre-flight checks
W0903 04:43:18.157247 9528 checks.go:1049]
[preflight] WARNING: Couldn't create the interface used for talking to the container runtime:
failed to create new CRI runtime service: validate service connection: validate CRI v1 runtime API
for endpoint "unix:///var/run/containerd/containerd.sock": rpc error: code = Unimplemented desc =
unknown service runtime.v1.RuntimeService
해결법:
rm /etc/containerd/config.toml
systemctl restart containerd
kubeadm init
출처: <https://virusuk.tistory.com/8> [virusuk:티스토리]
해결법2:
vi /etc/containerd/config.toml
#disabled_plugins = ["cri"]
systemctl restart containerd
2.kube-proxy 상태가 crashloopback 일때
해결법:
containerd config default | tee /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
service containerd restart
service kubelet restart
3.kube-flannel: pod cidr 할당 관련( Error registering network: failed to acquire lease: node "c2-worker1" pod cidr not assigned)
1.실행
sudo modprobe br_netfilter
2.파일 수정
sudo nano /etc/sysctl.d/99-sysctl.conf
#Add the following lines to the file
net.bridge.bridge-nf-call-ip6tables=1
net.bridge.bridge-nf-call-iptables=1
net.ipv4.ip_forward=1
sysctl 설정 적용
sudo sysctl --system
restart kublet
sudo systemctl restart kubelet
tailscale을 이용한 클러스터 구축 중 발생하는 kube-flannel 관련 에러 발생시
kube-flannel 기본ip 대역으로 설정했다면 밑의 명령어를 실행하면 해결됩니다.
sudo ip route add 10.96.0.0/12 dev tailscale0 개선할 점.
Nginx-ingress SPOF
현재 nginx-ingress pod(hostnetwork mode)는 1개로 plaid-k8s-worker1 노드에 고정된 상태입니다. GCP-L4 로드밸런서에서 plaid-k8s-worker1노드의 ip를 가르키도록 하였고 모든 접속은 plaid-k8s-worker1을 거쳐서 들어오기에 이 노드가 Down되면 모든 서비스 접속이 불가능해지는 SPOF문제가 존재하기에
개선이 필요.모니터링 시스템 구축 및 알럿 시스템 고도화
tailscale 의존성 대비
istio 고도화를 통한 서비스간 통신 보안 및 접근 제어