Kubernetes에 대한 새로운 블로그 시리즈의 세 번째 글에 오신 것을 환영합니다. 이 시리즈에서는 Zadara 환경에서 Kubernetes를 배포하는 방법과, AWS 호환 클라우드에서 컨테이너화된 애플리케이션을 관리하는 데 Kubernetes를 어떻게 활용할 수 있는지 살펴봅니다. Kubernetes를 처음 접하는 분이든 이미 경험이 있는 사용자든, 이 시리즈는 Zadara 클라우드에서 컨테이너화된 애플리케이션을 관리하는 데 유용한 인사이트와 모범 사례를 제공할 것입니다. 앞으로도 흥미로운 콘텐츠를 기대해 주세요.
이번 블로그 글에서는 Kubernetes 클러스터 위에서 실제 워크로드를 실행하는 방법에 초점을 맞추겠습니다. 잘 알려진 블로그 및 콘텐츠 관리 플랫폼인 WordPress를 배포함으로써, Kubernetes의 기본 기능은 물론 EKS-D 내장 애드온을 통한 매끄러운 클라우드 통합까지 포함하여 Zadara 클라우드에서 Kubernetes가 제공하는 강점을 보여드리겠습니다.
WordPress 배포
이번 데모에서는 WordPress용 Bitnami Helm Chart를 사용합니다. Bitnami Chart는 문서화가 잘되어 있고, 엔터프라이즈 환경에서도 사용할 수 있는 프로덕션급 배포 솔루션으로 알려져 있습니다. WordPress Chart는 이러한 사용 사례를 잘 보여주는 예입니다. 이 Chart에는 WordPress 자체뿐 아니라 통합 데이터베이스인 MariaDB, 캐시 저장소인 Memcached, 그리고 로드 밸런싱 및 확장 관련 옵션 등 다양한 Bitnami 기능이 포함되어 있으며, 이번 데모에서 이를 활용할 예정입니다. 아래 다이어그램은 Chart의 기본 구성을 단순화해 표현한 것입니다. 클라우드 통합과 직접 관련이 없는 ServiceAccount, Secrets 등의 리소스는 생략했습니다.
WordPress 자체는 Kubernetes Deployment이며 Memcached PVC와 연결되어 있습니다. 반면 MariaDB는 Kubernetes StatefulSet이며 데이터베이스 PVC와 연결되어 있습니다. 두 구성 요소 모두 통신을 위해 Kubernetes Service를 사용하지만, MariaDB는 WordPress가 내부적으로 사용하는 ClusterIP를 사용하고, WordPress 자체는 외부 사용자가 접근할 수 있도록 LoadBalancer를 사용합니다.
먼저 값을 수정하지 않은 기본 상태 그대로 Helm Chart를 배포해 보겠습니다. 이는 어떤 리소스들이 생성되는지 확인하기 위한 목적입니다. kubectl이 Kubernetes 클러스터와 통신할 수 있도록 설정되어 있다고 가정합니다. 예를 들어 KUBECONFIG 환경 변수 또는 기타 방법으로 설정되어 있다면, Helm 역시 동일한 설정을 사용합니다.
helm install wp oci://registry-1.docker.io/bitnamicharts/wordpress –namespace wordpress –create-namespace
이 명령은 모든 기본값(Default Values)을 사용하여 WordPress Chart를 설치하며, wordpress 네임스페이스 내부에 wp라는 이름의 Helm Release로 배포합니다. 또한 wordpress 네임스페이스가 존재하지 않는 경우 자동으로 생성됩니다.
WordPress 네임스페이스 내부에서 Chart에 의해 여러 리소스가 생성된 것을 확인할 수 있습니다. 아래에서는 앞서 설명한 아키텍처 다이어그램에 언급된 Pod, PVC, Service 등을 볼 수 있습니다.
포드가 k8s-worker-1에서 실행되도록 예약되었지만 아직 준비되지 않았습니다. 포드가 준비되는 모습을 지켜보면서 몇 초 더 기다려 보겠습니다.
이제 Pod가 준비되었으므로 WordPress에 접근할 수 있어야 합니다. 일반적으로는 방금 생성된 LoadBalancer Service를 통해 접근하게 됩니다. 하지만 여기에는 한 가지 주의할 점이 있습니다. 기본적으로, 별도의 설정이 없는 경우 AWS Load Balancer Controller(Kubernetes 리소스를 클라우드 수준의 Load Balancer로 변환하는 역할)는 Public-facing Load Balancer가 아니라 Internal-facing Load Balancer를 생성합니다. 이는 AWS API 설계상 존재하는 제한 사항이며, Zadara의 AWS 호환 API 역시 이를 동일하게 구현하고 있습니다. 따라서 생성된 Load Balancer는 내부 IP와 내부 DNS만 가지게 됩니다.
이 Load Balancer는 VPC 외부에서는 사용할 수 없지만, Target Group을 통해 특정 포트 요청이 해당 워커 노드의 포트로 전달되는 것을 확인할 수 있습니다. 예를 들어 아래와 같이 80번 포트 요청이 관련 워커 노드로 포워딩됩니다.
이 단계에서 WordPress는 Kubernetes 클러스터 내부에서 실행되고 있지만, 해당 Load Balancer를 사용하려면 Bastion VM과 같이 VPC 내부에 있는 다른 VM을 통해 접근해야 합니다. 또는 Bastion VM을 경유하거나 Kubernetes Pod에 직접 포트 포워딩을 설정하여 로컬 브라우저에서 WordPress에 접속할 수도 있습니다. 하지만 이는 애플리케이션을 실제 서비스 형태로 사용하는 방식이라고 보기는 어렵습니다. 따라서 Internal-facing Load Balancer가 아니라 Internet-facing Load Balancer가 필요합니다.
로드 밸런싱 다시 살펴보기
앞서 Kubernetes Service가 클라우드 NLB, 즉 Network Load Balancer로 변환되는 방식을 확인했습니다. 하지만 Kubernetes와 AWS/Zadara 모두 기존 Service/NLB를 직접 수정하는 것은 허용하지 않습니다. 대신 기존 Service를 삭제하면 연결된 NLB도 함께 삭제됩니다. 이후 전용 values 파일을 사용해 Helm Release를 관련 설정으로 업데이트하여 Public-facing Load Balancer를 새로 생성하겠습니다.
kubectl delete service wp-wordpress –namespace wordpress
cat > ./values-wordpress.yaml << “EOF”
service: annotations:
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
EOF
helm upgrade wp oci://registry-1.docker.io/bitnamicharts/wordpress –namespace wordpress -f values-wordpress.yaml
여기서는 Chart 문서에 명시된 기존 values 속성을 기반으로 values 파일을 생성했습니다. 그리고 Load Balancer Controller의 Service 문서에 따라 scheme annotation에 internet-facing 값을 사용했습니다. 이를 통해 새로 생성되는 NLB는 외부 인터넷에서도 접근할 수 있게 됩니다.
몇 분 내로 새 Load Balancer가 사용할 준비를 마치게 됩니다. Service annotation은 Load Balancer의 동작 방식을 제어하는 데 사용되며, 이번에는 외부 인터넷에서 접근할 수 있도록 Public IP 주소가 할당됩니다.
다만 AWS와 달리 Zadara 클라우드는 Public DNS 서비스를 제공하지 않기 때문에 External DNS 이름은 사용할 수 없습니다. 대신 사용자가 사용하는 DNS 제공업체에서 해당 Public IP에 대한 A 레코드를 생성하거나, Public IP를 직접 사용하여 인터넷을 통해 애플리케이션에 접근할 수 있습니다.
이번 예제에서는 직접 IP를 사용했기 때문에 보안 연결을 사용하지 않았다는 점을 확인하셨을 것입니다. 또한 Zadara의 NLB는 현재 도메인 인증서를 지원하지 않습니다. 하지만 이는 ALB, 즉 Layer 7 Load Balancer를 소개하기에 적절한 사례입니다. ALB는 Service가 아니라 Ingress 리소스를 통해 Kubernetes와 연동됩니다. 이러한 Load Balancer는 도메인 인증서를 지원하므로, 인증서를 클라우드로 가져온 뒤 나중에 Ingress 리소스를 정의할 때 사용할 수 있습니다.
이제 현재 Load Balancer Service를 삭제하고, Chart 문서와 Load Balancer Controller의 Ingress 문서를 기반으로 Helm values 파일을 다시 구성하겠습니다. 이를 통해 해당 인증서 ID를 사용하는 Internet-facing Ingress 리소스를 생성할 수 있습니다.
kubectl delete service wp-wordpress –namespace wordpress
cat > ./values-wordpress.yaml << “EOF”
service:
type: NodePort
ingress: enabled: true annotations: alb.ingress.kubernetes.io/certificate-arn: 68b5f357-b25f-4a07-aa8b-2797305a57db alb.ingress.kubernetes.io/scheme: internet-facing
hostname: wordpress path: /*
EOF
helm upgrade wp oci://registry-1.docker.io/bitnamicharts/wordpress –namespace wordpress -f values-wordpress.yaml
Ingress도 포트를 통해 트래픽을 라우팅하려면 여전히 Service가 필요하다는 점에 유의해야 합니다. 기존 LoadBalancer Service를 그대로 둘 수도 있지만, 여기서는 클라우드 Load Balancer가 아닌 노드 수준의 포트 구성 방식인 NodePort Service로 변경했습니다. 이 방식도 동일하게 트래픽 라우팅 역할을 수행할 수 있으며, 추가적인 클라우드 리소스를 사용하지 않는다는 장점이 있습니다.
이번에는 Load Balancer가 Internet-facing ALB로 생성되며, HTTPS 프로토콜로 트래픽을 수신하게 됩니다.
ALB에는 인증서 및 Chart에서 정의한 기타 설정들을 반영하기 위한 Listener Rule이 미리 구성되어 있습니다. 예를 들어 애플리케이션에 접근하려면 hostname이 반드시 `wordpress`여야 합니다.
이 때문에 Public IP를 그대로 사용하는 것만으로는 보안 연결(HTTPS)을 사용할 수 없습니다. 인증서는 올바른 도메인 이름과 함께 사용될 때만 유효하며, ALB 역시 `wordpress`가 아닌 다른 hostname은 허용하지 않습니다. 따라서 완전한 HTTPS 구성을 위해서는 사용 중인 DNS 제공업체에서 해당 Public IP에 대한 A 레코드를 생성해야 합니다. 설정이 완료되면 `https://wordpress.zadara-qa.com`으로 접속할 수 있으며, 이제 연결이 정상적으로 보안 처리된 것을 확인할 수 있습니다.
Kubernetes 확장
Kubernetes는 변화하는 요구사항에 맞춰 환경을 동적으로 확장할 수 있는 다양한 방법을 제공합니다. 예를 들어 피크 시간대에도 애플리케이션이 안정적으로 응답하도록 보장할 수 있습니다. 이번 데모에서는 클라우드 통합을 활용하는 방식, 즉 필요에 따라 데이터 플레인인 Worker ASG 노드를 확장하거나 축소하는 방법에만 초점을 맞추겠습니다. Horizontal/Vertical Pod Scaling이나 Pod Anti-Affinity Rule과 같은 다른 방법들은 Kubernetes 클러스터 내부에서 동작하는 방식이므로, 이번 데모에서는 다루지 않습니다. 클러스터 확장을 강제로 유도하는 한 가지 방법은 현재 워커 노드가 새 Pod를 받지 못하도록 `cordon` 처리한 뒤, 새 Pod 생성을 시도하는 것입니다. 예를 들어 MariaDB StatefulSet을 단일 Pod 구성에서 다중 Pod 구성으로 확장하기 위해 replica count를 2로 설정해 보겠습니다.
kubectl cordon k8s-worker-1
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
kubectl scale –replicas=2 statefulset/wp-mariadb –namespace wordpress
kubectl get pods,pvcs –namespace wordpress
이제 워커 노드에 `NoSchedule` taint가 설정된 것을 확인할 수 있습니다. 이는 마스터 노드와 유사하지만, 적용된 이유는 서로 다릅니다.
새로운 Pod는 실행 가능한 노드를 기다리면서 `Pending` 상태에 있는 것을 확인할 수 있습니다. 마찬가지로 새로운 PVC 역시 Pod를 기다리며 `Pending` 상태가 됩니다. 이는 EBS CSI가 해당 인스턴스에 볼륨을 연결하는 작업을 보류하고 있기 때문입니다.
Zadara의 EKS-D 솔루션에는 `cluster-autoscaler` 애드온이 기본 포함되어 있습니다. 이 기능은 사용 가능한 노드에 스케줄링되지 못한 Pending Pod를 감지하고, 사전에 설정된 최소값과 최대값 범위 내에서 Worker ASG의 Desired Capacity를 자동으로 조정합니다.
ASG는 이러한 신호를 감지하여 ASG의 Desired Capacity를 업데이트하고, 그 결과 새로운 Worker 인스턴스를 생성하게 됩니다.
몇 분 후 새로운 Worker 노드가 Kubernetes 클러스터에 합류하고 `Ready` 상태가 됩니다. 이후 Pod는 사용 가능한 해당 노드에 정상적으로 스케줄링되며, 새로운 PVC 역시 이 노드에 연결된 새로운 볼륨에 바인딩됩니다.
스케일 다운(축소)이 어떻게 동작하는지 궁금하다면, 첫 번째 Worker 노드에 대해 `uncordon`을 수행한 뒤 약 10분 정도 기다리면 됩니다. 이는 cluster-autoscaler의 기본 scale-down 대기 시간(inactivity window)입니다. 추가로 생성된 노드는 삭제 후보 상태가 되면서 `PreferNoSchedule` taint가 설정됩니다. 이후 scale-down 대기 시간이 만료되면, 두 번째 MariaDB Pod는 해당 PVC와 기반 볼륨(Underlying Volume)을 포함하여 자동으로 첫 번째 Worker 노드로 이동하게 되며, 그 결과 기존 추가 노드는 안전하게 삭제될 수 있습니다.
Persistence에 대해 간단히 살펴보기
이번 데모 전반에서는 애플리케이션이 스토리지를 필요로 한다는 점을 자연스럽게 전제로 진행했습니다. 그 이유는 기본 제공되는 EBS CSI 설정이 별도 지정이 없는 한 모든 영속성(Persistence) 요구사항에 대해 기본 StorageClass로 자동 적용되기 때문입니다.
실제로 Helm Chart의 일부로 생성된 모든 PVC는 각각에 해당하는 볼륨에 바인딩되어 있습니다. 이 볼륨들은 애플리케이션 Pod가 실행되고 있는 동일한 Worker 노드에 연결되어 있습니다.
기본적으로 활성화되어 있는 EBS CSI의 마지막 기능 중 하나는 기존 PVC의 요청 용량(Requested Capacity)을 확장할 수 있다는 점입니다. 이는 Helm Release를 업데이트하거나, 기존 PVC를 직접 Patch하는 방식으로 변경할 수 있습니다. 어떤 방식을 사용하든 요청 이후 기반 PV(Underlying PV)는 자동으로 확장되며, PVC 크기 역시 새로운 용량으로 반영됩니다.
이렇게 해서 WordPress 볼륨은 10GB에서 12GB로 확장되었습니다. 다만 설계상 기존 PVC는 확장만 가능하며, 용량을 줄이는 것은 지원되지 않습니다. 그 외에는 사용자가 별도로 신경 쓸 필요 없는 또 하나의 매끄러운 클라우드 통합 기능이라고 볼 수 있습니다. 이러한 작업은 EKS-D 솔루션이 자동으로 처리해 줍니다.
마무리
이번 블로그에서는 표준 도구와 일반적인 방법론을 사용하여 WordPress를 대표적인 Kubernetes 워크로드 형태로 배포해 보았습니다. Helm이 실제 애플리케이션 배포를 담당했으며, 내장된 EKS-D 애드온은 Volume, Load Balancer, Auto Scaling Group과 같은 클라우드 리소스를 매끄럽게 통합하여 처리해 주었습니다. 이제 워크로드가 정상적으로 실행되고 있으므로, 다음 블로그에서는 Backup 및 Restore 활용 사례에 대해 다룰 예정입니다. 많은 기대 부탁드립니다.
Zadara Korea 테크 에반젤리스트의 추가 의견
이번 블로그는 단순히 WordPress를 Kubernetes 위에 배포하는 수준을 넘어, Zadara의 AWS 호환 클라우드 환경에서 Kubernetes가 얼마나 자연스럽게 클라우드 리소스와 통합될 수 있는지를 잘 보여주는 사례입니다. 특히 EKS-D 기반의 기본 애드온을 통해 Load Balancer, Persistent Volume, Auto Scaling과 같은 기능이 Kubernetes 워크플로우와 매끄럽게 연결되는 점은 운영 효율성과 확장성 측면에서 큰 강점입니다. 또한 Internal/Internet-facing Load Balancer 구성, ALB 기반 HTTPS Ingress, Cluster Autoscaler 연동까지 실제 운영 환경에서 자주 요구되는 시나리오를 포함하고 있어, 단순 실습을 넘어 엔터프라이즈 Kubernetes 운영 모델에 가까운 접근 방식을 보여준다는 점에서도 의미가 있습니다.
관련 PoC가 필요하신 분들은 Zadara Korea로 문의해 주시기 바랍니다.
-
자다라(Zadara) 기술 블로그 번역 및 기술 검토: Zadara Korea 테크 에반젤리스트 양대영
Zadara Korea 테크 에반젤리스트의 추가 의견
이번 블로그는 단순히 WordPress를 Kubernetes 위에 배포하는 수준을 넘어, Zadara의 AWS 호환 클라우드 환경에서 Kubernetes가 얼마나 자연스럽게 클라우드 리소스와 통합될 수 있는지를 잘 보여주는 사례입니다. 특히 EKS-D 기반의 기본 애드온을 통해 Load Balancer, Persistent Volume, Auto Scaling과 같은 기능이 Kubernetes 워크플로우와 매끄럽게 연결되는 점은 운영 효율성과 확장성 측면에서 큰 강점입니다. 또한 Internal/Internet-facing Load Balancer 구성, ALB 기반 HTTPS Ingress, Cluster Autoscaler 연동까지 실제 운영 환경에서 자주 요구되는 시나리오를 포함하고 있어, 단순 실습을 넘어 엔터프라이즈 Kubernetes 운영 모델에 가까운 접근 방식을 보여준다는 점에서도 의미가 있습니다.
관련 PoC가 필요하신 분들은 Zadara Korea로 문의해 주시기 바랍니다.