컨테이너 오케스트레이션의 대표 주자인 쿠버네티스(Kubernetes)를 Kubespray를 이용해 구축하는 방법에 대해 알아보겠습니다. 레거시 서버 구성에서 컨테이너 기반으로 전환하는 과정과 그 이유, 그리고 실제 구축 경험을 공유하고자 합니다.
왜 컨테이너 기반으로 전환했나?
기존의 레거시 서버 구성(VM 호스트 OS 위에 직접 서버 구성, 1대의 VM이 하나의 서버 환경)은 다음과 같은 한계가 있었습니다:
- 확장성의 제약: 트래픽 증가 시 빠른 대응이 어려움
- 장애 복구의 어려움: 서버 다운 시 복구 과정이 복잡하고 시간 소요가 큼
- 리소스 활용의 비효율: VM 단위로 리소스가 고정되어 낭비 발생
처음에는 도커(Docker) 기반 구성을 고려했지만, 더 확장성 있고 장애 대응에 유연한 환경을 구축하기 위해 컨테이너 오케스트레이션 도구인 쿠버네티스 도입을 결정했습니다.
쿠버네티스란?
쿠버네티스는 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화하는 오픈소스 시스템입니다. 구글에서 개발하여 CNCF(Cloud Native Computing Foundation)로 이관되었으며, 현재 클라우드 네이티브 아키텍처의 핵심 요소로 자리잡았습니다.
쿠버네티스 클러스터의 기본 구성
쿠버네티스 클러스터는 크게 마스터 노드와 워커 노드로 구성됩니다:
-
마스터 노드(Master Node): 클러스터를 제어하는 컨트롤 플레인 역할
- kube-apiserver: 쿠버네티스 API를 제공하는 핵심 컴포넌트
- kube-controller-manager: 클러스터 상태를 모니터링하고 변경사항 처리
- kube-scheduler: 새로운 워크로드를 적절한 노드에 배치
- etcd: 클러스터의 모든 상태 정보를 저장하는 분산 Key-Value 저장소
-
워커 노드(Worker Node): 실제 애플리케이션이 실행되는 노드
- kubelet: 노드의 컨테이너 생성/관리
- kube-proxy: 네트워크 규칙 관리
- Container Runtime: Docker 등의 컨테이너 실행 환경
기본적으로 최소 1개의 마스터 노드와 1개 이상의 워커 노드가 필요합니다. 마스터 노드에도 컨테이너를 구동할 수 있지만, 역할 분리를 위해 일반적으로 워커 노드에만 애플리케이션 파드(Pod)를 배치합니다.
우리의 쿠버네티스 클러스터 구성
노드 구성
- 마스터 노드: 1대 (추후 HA 구성 예정)
- 워커 노드: 3대 (Develop, Staging, Collabo 환경용)
워커 노드별 컨테이너 구성
각 워커 노드(Develop, Staging, Collabo)는 다음과 같은 컨테이너를 공통적으로 구동합니다:
- Gameserver(php-fpm)
- Adam
- tool-apiserver
- DB (qa-db, real-db)
HA(High Availability) 구성 계획
현재는 단일 마스터 노드로 구성되어 있지만, 추후 고가용성 확보를 위해 마스터 노드를 여러 대로 구성할 예정입니다.
HA(High Availability)란? 시스템이 중단 없이 지속적으로 운영될 수 있도록 하는 기술입니다. 마스터 노드가 1개일 경우, 해당 노드에 장애가 발생하면 전체 클러스터 관리가 불가능해집니다. 따라서 마스터 노드를 2개 이상(일반적으로 홀수로 구성)으로 구성하여 일부 노드에 장애가 발생해도 클러스터 운영에 문제가 없도록 합니다.
Kubespray를 선택한 이유
쿠버네티스 클러스터를 구축하는 방법은 다양하지만, 우리는 Kubespray를 선택했습니다. Kubespray는 다음과 같은 장점이 있습니다:
- 다양한 인프라 지원: 베어메탈, VM, 클라우드 환경 등 다양한 인프라에서 쿠버네티스 배포 가능
- Ansible 기반 자동화: 인프라를 코드로 관리하여 일관된 배포와 관리 가능
- 고가용성 구성 지원: 마스터 노드의 HA 구성을 쉽게 설정 가능
- 커스터마이징 용이: 필요한 구성요소만 선택적으로 설치 가능
- 클러스터 업그레이드 지원: 운영 중인 클러스터의 버전 업그레이드를 쉽게 수행 가능
상세 설치 과정
Kubespray를 이용한 쿠버네티스 클러스터 설치는 다음과 같은 단계로 진행됩니다:
1. 사전 요구사항
모든 노드에서의 준비:
# 모든 서버에서 사용자 생성 및 sudo 권한 부여
adduser kube
passwd kube
usermod -aG sudo kube
# swapoff 설정
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 방화벽 설정 확인 및 필요한 포트 오픈
sudo ufw status
sudo ufw allow 6443/tcp # Kubernetes API server
sudo ufw allow 2379:2380/tcp # etcd server client API
sudo ufw allow 10250/tcp # Kubelet API
sudo ufw allow 10251/tcp # kube-scheduler
sudo ufw allow 10252/tcp # kube-controller-manager
sudo ufw allow 10255/tcp # Read-only Kubelet API
배포 서버(Kubespray를 실행할 서버) 준비:
# Python 3 및 pip 설치
sudo apt update
sudo apt install -y python3 python3-pip
# Ansible 설치
sudo pip3 install ansible
# 배포 서버에서 모든 노드로의 SSH 키 복사
ssh-keygen -t rsa
ssh-copy-id kube@master-node-ip
ssh-copy-id kube@worker1-node-ip
ssh-copy-id kube@worker2-node-ip
ssh-copy-id kube@worker3-node-ip
2. Kubespray 다운로드 및 설정
# Kubespray 저장소 클론
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
# 필요한 Python 패키지 설치
pip3 install -r requirements.txt
# 샘플 인벤토리를 복사하여 사용자 환경에 맞게 설정
cp -rfp inventory/sample inventory/mycluster
3. 인벤토리 설정
inventory/mycluster/hosts.yaml
파일을 수정하여 클러스터 구성을 정의합니다:
all:
hosts:
master:
ansible_host: 192.168.1.10
ip: 192.168.1.10
access_ip: 192.168.1.10
worker1:
ansible_host: 192.168.1.11
ip: 192.168.1.11
access_ip: 192.168.1.11
worker2:
ansible_host: 192.168.1.12
ip: 192.168.1.12
access_ip: 192.168.1.12
worker3:
ansible_host: 192.168.1.13
ip: 192.168.1.13
access_ip: 192.168.1.13
children:
kube_control_plane:
hosts:
master:
kube_node:
hosts:
worker1:
worker2:
worker3:
etcd:
hosts:
master:
k8s_cluster:
children:
kube_control_plane:
kube_node:
calico_rr:
hosts: {}
4. 클러스터 설정 커스터마이징
환경에 맞게 inventory/mycluster/group_vars/
디렉토리의 설정 파일을 수정합니다:
k8s_cluster/k8s-cluster.yml:
# 쿠버네티스 네트워크 설정
kube_network_plugin: calico
kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18
# 컨테이너 런타임 설정
container_manager: containerd
# 쿠버네티스 버전 설정
kube_version: v1.24.0
# 추가 기능 활성화
dashboard_enabled: true
helm_enabled: true
ingress_nginx_enabled: true
metallb_enabled: true
addons/metallb.yml (MetalLB 설정):
metallb_ip_range:
- "192.168.1.50-192.168.1.99"
metallb_protocol: "layer2"
5. 클러스터 배포 실행
# Ansible playbook 실행
ansible-playbook -i inventory/mycluster/hosts.yaml cluster.yml -b -v
6. 배포 확인
설치가 완료되면 마스터 노드에서 클러스터 상태를 확인합니다:
# kubeconfig 파일 설정
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 노드 상태 확인
kubectl get nodes
# 시스템 파드 상태 확인
kubectl get pods -n kube-system
설치 후 필수 설정
쿠버네티스 클러스터를 설치한 후에는 다음과 같은 추가 설정이 필요합니다:
1. 스토리지 클래스 설정
NFS 서버를 활용한 PersistentVolume 프로비저닝을 위해 NFS 클라이언트 프로비저너를 설치합니다:
# Helm 저장소 추가
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update
# NFS 프로비저너 설치
helm install nfs-client nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=nfs-server-ip \
--set nfs.path=/exported/path \
--set storageClass.name=nfs-client \
--set storageClass.defaultClass=true
2. Ingress 컨트롤러 설정
Ingress-Nginx가 설치되었다면, 필요한 추가 설정을 합니다:
# SSL 인증서 생성 (자체 서명 인증서 예시)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=example.com"
# 인증서를 쿠버네티스 시크릿으로 저장
kubectl create secret tls example-tls --key tls.key --cert tls.crt -n ingress-nginx
# Ingress 기본 설정 업데이트
kubectl edit configmap nginx-configuration -n ingress-nginx
필요한 설정을 추가합니다:
data:
ssl-protocols: "TLSv1.2 TLSv1.3"
use-forwarded-headers: "true"
proxy-body-size: "50m"
3. MetalLB 구성 완료
MetalLB가 설치되었다면, 외부 접근을 위한 설정을 확인하고 필요에 따라 업데이트합니다:
# ConfigMap 확인
kubectl get configmap -n metallb-system
# 필요시 IP 주소 범위 업데이트
kubectl edit configmap config -n metallb-system
4. 네임스페이스 생성
개발, 스테이징, 협업 환경을 위한 네임스페이스를 생성합니다:
# 네임스페이스 생성
kubectl create namespace develop
kubectl create namespace staging
kubectl create namespace collabo
# 리소스 쿼터 설정 (선택사항)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: develop
spec:
hard:
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
EOF
5. GitLab Runner 설정
CI/CD 파이프라인을 위한 GitLab Runner를 설정합니다:
# GitLab Runner Helm 차트 설치
helm repo add gitlab https://charts.gitlab.io
helm repo update
# Runner 설치
helm install gitlab-runner gitlab/gitlab-runner \
--namespace gitlab \
--create-namespace \
--set gitlabUrl=https://your-gitlab-instance.com \
--set runnerRegistrationToken=your-registration-token \
--set rbac.create=true
6. 모니터링 시스템 설정
클러스터 모니터링을 위한 Prometheus와 Grafana를 설치합니다:
# Prometheus 스택 설치
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set grafana.adminPassword=admin
7. 클러스터 백업 구성
etcd 데이터베이스 백업을 위한 스크립트 설정:
# 백업 스크립트 작성
cat <<EOF > /usr/local/bin/etcd-backup.sh
#!/bin/bash
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/etcd-snapshot-\$(date +%Y%m%d-%H%M%S).db
EOF
chmod +x /usr/local/bin/etcd-backup.sh
# cron 작업으로 등록
(crontab -l 2>/dev/null; echo "0 3 * * * /usr/local/bin/etcd-backup.sh") | crontab -
운영 환경 구성 사례
실제 서비스 환경 구성을 위해 다음과 같은 추가 설정을 고려할 수 있습니다:
1. 게임 서버 배포 예시 (php-fpm)
apiVersion: apps/v1
kind: Deployment
metadata:
name: gameserver
namespace: develop
spec:
replicas: 3
selector:
matchLabels:
app: gameserver
template:
metadata:
labels:
app: gameserver
spec:
containers:
- name: php-fpm
image: php:8.1-fpm
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: game-code
mountPath: /var/www/html
volumes:
- name: game-code
persistentVolumeClaim:
claimName: game-code-pvc
---
apiVersion: v1
kind: Service
metadata:
name: gameserver-service
namespace: develop
spec:
selector:
app: gameserver
ports:
- port: 9000
targetPort: 9000
2. 데이터베이스 설정 예시
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: qa-db
namespace: develop
spec:
serviceName: "qa-db"
replicas: 1
selector:
matchLabels:
app: qa-db
template:
metadata:
labels:
app: qa-db
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: root-password
ports:
- containerPort: 3306
volumeMounts:
- name: qa-db-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: qa-db-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "nfs-client"
resources:
requests:
storage: 10Gi
3. 환경별 Ingress 설정
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: develop-ingress
namespace: develop
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- develop.example.com
secretName: develop-tls
rules:
- host: develop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gameserver-service
port:
number: 80
클러스터 관리 및 유지보수 팁
1. 클러스터 업그레이드
Kubespray를 사용하여 클러스터 버전을 업그레이드하는 방법:
# Kubespray 최신 버전으로 업데이트
cd kubespray
git pull
# 인벤토리 파일에서 원하는 쿠버네티스 버전 설정
vi inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
# kube_version 값을 변경
# 업그레이드 실행
ansible-playbook -i inventory/mycluster/hosts.yaml upgrade-cluster.yml -b -v
2. 노드 추가하기
클러스터에 새 노드를 추가하는 방법:
# 인벤토리 파일에 새 노드 추가
vi inventory/mycluster/hosts.yaml
# 노드 추가 플레이북 실행
ansible-playbook -i inventory/mycluster/hosts.yaml scale.yml -b -v
3. 노드 유지보수
노드 유지보수를 위한 drain(비우기) 및 복구:
# 노드 drain
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# 유지보수 작업 수행 후 노드 복구
kubectl uncordon <node-name>
4. 클러스터 상태 모니터링
# 노드 상태 확인
kubectl get nodes -o wide
# 시스템 파드 상태 확인
kubectl get pods -n kube-system
# 모든 네임스페이스의 파드 상태 확인
kubectl get pods --all-namespaces
# 리소스 사용량 확인
kubectl top nodes
kubectl top pods --all-namespaces
쿠버네티스 클러스터에 추가할 주요 컴포넌트
1. Ingress-Nginx
외부에서 클러스터 내 서비스로의 접근을 관리하는 인그레 스 컨트롤러:
# Helm으로 설치
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace
2. MetalLB
온프레미스 환경에서 로드밸런서 서비스 타입 구현:
# MetalLB 설치
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
# IP 주소 풀 설정
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.50-192.168.1.99
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: l2-advert
namespace: metallb-system
spec:
ipAddressPools:
- first-pool
EOF
마치며
Kubespray를 통한 쿠버네티스 클러스터 구축은 초기 설정과 몇 가지 추가 구성이 필요하지만, 그 결과로 확장성이 뛰어나고 장애에 강한 인프라를 얻을 수 있습니다. 특히 운영 환경에서는 마스터 노드의 HA 구성을 통해 더욱 안정적인 서비스를 제공할 수 있습니다.
지금까지 설명한 구성은 기본적인 설치 과정과 주요 컴포넌트 설정에 초점을 맞추었습니다. 실제 환경에서는 보안 정책, 네트워크 정책, 리소스 관리 등 추가적인 요소를 고려해야 합니다.
쿠버네티스를 처음 도입하시는 분들께는 학습 곡선이 있을 수 있지만, 일단 익숙해지면 컨테이너 기반의 마이크로서비스 아키텍처의 모든 이점을 누릴 수 있습니다. 무엇보다 자동화된 스케일링, 셀프힐링, 선언적 배포 등의 기능을 통해 운영 부담을 크게 줄일 수 있습니다.
앞으로도 쿠버네티스 관련 다양한 팁과 경험을 공유하도록 하겠습니다. 궁금한 점이나 추가 정보를 원하시면 댓글로 남겨주세요!