쿠버네티스 (Kubernetes) 를 이용한 클러스터링 정리 및 실습 (1)

Gunhee Jang·2023년 1월 3일
0

컨테이너 기술

목록 보기
2/2

지난 포스팅을 통해 도커 스웜 에 대해서 알아본 데에 이어, 본격적으로 쿠버네티스에 대해서 다뤄보고자 한다. 본 포스팅에서는 쿠버네티스를 이용하여 간단하게 컨테이너들이 클러스터에 등록되고 서비스를 동작시키는 정도 까지 다뤄보고자 한다.

쿠버네티스란?

우선 쿠버네티스에 대해서 정리를 먼저 해보자면, 도커 스웜과 마찬가지로 컨테이너로 구성된 시스템에서 모듈들을 하나의 자원 풀로 만들어 관리하는 오케스트레이션 도구이다. 그 중에서도 쿠버네티스는 성능과 안정성 면에서 매우 뛰어난 결과를 보이면서도, 스케줄링이나 장애 복구, 오토 스케일링, 서비스 디스커버리, 인그레스 등 컨테이너 관리를 위한 기능 및 컴포넌트를 사용자가 직접 다룰 수 있다는 점에서 주목을 받고 대부분의 서비스에서 이용되고 있다.

특히 쿠버네티스는 다양한 클라우드 운영 도구들과의 연동성이 좋아 클라우드 환경에서 많이 사용되는데, AWS의 ELK나 GCP의 GKE 와 같이 Managed Kubernetes 를 이용하여 간편하게 관리할 수 있다.

혹은 온프레미스 방식으로 Host PC에 쿠버네티스를 설치하는 경우 Managed 방식에 비해 의존성을 낮추어 많은 기능들을 커스텀하여 이용할 수 있지만, 이는 관리의 복잡도가 매우 높아지기 때문에 대부분의 일반적인 서비스에서는 클라우드의 Managed 서비스를 이용하게 된다.

쿠버네티스의 특징을 몇 가지 정리해 보자면 다음과 같은 내용들이 있다.

  • Pods, Nodes, Replica Set, Deployment 등 모든 리소스가 오브젝트 형태로 관리된다.
  • YAML 파일을 주로 사용하여 관리한다.
  • Docker를 포함하여 API 서버, 컨트롤러 매니저, 스케줄러, 프록시, 네트워크 플러그인 등 컴포넌트들이 실행되며, 이들이 모여 쿠버네티스를 이룬다.

실습에 앞서, 쿠버네티스 시스템을 이루는 오브젝트에 대해 아주 간단하게만 짚고 넘어가보고자 한다.

  1. Pods : 컨테이너 애플리케이션의 기본 단위로, 1개 이상의 컨테이너로 구성된 컨테이너의 집합
  2. Replica Set : 정해진 수의 동일한 포드가 항상 실행되도록 관리하는 오브젝트
    • YAML 파일을 통해 서로 다른 이름으로 포드를 정의하여 실행시킬 수 있지만, 이는 매우 비효율적이다.
  3. Deployment : Replica Set과 비슷한 동작을 하지만, App의 업데이트와 배포를 더욱 편하게 할 수 있는 오브젝트
    • 업데이트 시 레플리카셋의 변경 사항을 저장하는 Revision 을 남겨서 롤백이 가능하게 하며, 무중단 서비스를 위해 롤링 업데이트 전략을 지정할 수도 있다.

컨테이너 준비

먼저, 실습에 이용될 컨테이너 이미지를 준비하도록 한다. 클러스터링이 정확하게 구성되며 트래픽이 각 컨테이너들로 분산되는 것을 보다 직관적으로 확인하기 위해, 웹 서버를 동작시키며 각 컨테이너의 Hostname이 출력되도록 하는 이미지를 준비하고자 한다.

따라서, 우선 ubuntu:20.04 베이스 이미지에 Node js를 설치하였다. 이후 Express 프레임워크를 이용하여 호스트 이름을 웹 페이지에 출력하도록 하였다. 페이지를 구성하는 index.js 파일은 다음과 같이 간단하게 구성하였다.

const express = require("express");
const app = express();
const port = 5001;

var name = process.env.HOSTNAME;

app.get("/", (req, res) => res.send(name));

app.listen(port, () =>
        console.log(`Example app listening at http://localhost:${port}`)
);

실행 결과는 다음과 같이, 5001번 포트 에서 앱이 동작하고 있음을 확인할 수 있다.

웹 브라우저에서 localhost 에 접속하여, 컨테이너의 호스트 이름이 정상적으로 출력되는 것을 확인하였다.

이제, 해당 컨테이너를 도커 이미지로 만들도록 한다. 이를 위해 우선 해당 컨테이너를 k8s_test:1.0 이미지로 commit 했으며, 컨테이너 실행 시 웹 서비스를 자동으로 실행시켜주는 이미지를 만들기 위해 다음과 같이 Dockerfile 을 구성하였다.

FROM k8s_test:1.0
WORKDIR /root/app
CMD ["npm", "start", "&"]
EXPOSE 5001

이후 다음 명령어를 통해 Dockerfile로부터 새로운 이미지를 만들었다.

$ docker build -t k8s_test:1.1 .

클러스터 구축

이제 생성 완료된 k8s_test:1.1 이미지를 이용해서 쿠버네티스 시스템을 구성하고자 한다. 우선 본 실습에서는 로컬 환경에서 클러스터를 구성할 예정이므로, 간단하게 minikube 를 설치해 두었다. 이후, 다음의 YAML 파일을 이용하여 먼저 Deployment 를 생성하였다.

  1 apiVersion: apps/v1
  2 kind: Deployment
  3 metadata:
  4   name: my-deployment
  5   labels:
  6     app: my-custom-deployment
  7 spec:
  8   replicas: 3
  9   selector:
 10     matchLabels:
 11       app: my-custom-web-service
 12   template:
 13     metadata:
 14       name: my-web-service
 15       labels:
 16         app: my-custom-web-service
 17     spec:
 18       containers:
 19       - name: my-web-service
 20         image: ghj5719/k8s_test:1.1
 21         ports:
 22         - containerPort: 5001

위에서 이미지를 준비할 때, Hostname을 출력하는 웹 서버를 5001번 포트 에서 동작하도록 하였으므로 내부 포트 5001번에 대한 명시를 해 주었다.

위의 파일을 이용하여 Deployment 를 생성하니 (kubectl apply -f [file]), 다음과 같이 대시보드에서도 정상 동작하고 있음을 확인할 수 있었다.

서비스 구성

여기까지 실행한 결과로도 각 Pods 에 구성된 컨테이너에 트래픽이 전달되는 것을 확인할 수 있지만, 본 포스트에서는 Service 개념까지 정리하고자 한다.

Deployment 를 통해 생성한 Pods 에 대해 IP를 이용해서 접근할 수는 있겠지만, 이는 로컬 개발 환경이나 클러스터 내부에서만 이용할 수 있다. 더불어 할당된 Pods 의 IP는 영속적이지 않으므로, 완벽한 동작을 위해서는 Discovery 방법이 필요하다.

쿠버네티스의 서비스는 형태에 따라 Type이 다르다. 특징에 대해 매우 간단하게 정리해 보자면 다음과 같다.

  1. ClusterIP 타입 : k8s 내부에서만 포드에 접근할 때 사용
  2. NodePort 타입 : 외부에서 포드에 접근할 때 사용
  3. LoadBalancer 타입 : 외부에서 포드에 접근할 때 사용, 클라우드 플랫폼 환경에서 이용

NodePort 서비스 타입의 경우 ClusterIP 서비스 기능을 포함하기 때문에, 내부 IP와 DNS 이름을 통해서 접근도 가능하다. NodePort 타입을 이용한 서비스 생성을 위해 다음의 YAML 파일을 이용하였다.

  1 apiVersion: v1
  2 kind: Service
  3 metadata:
  4   name: my-service
  5   labels:
  6     app: my-custom-service
  7 spec:
  8   ports:
  9     - name: web-port
 10       port: 40001
 11       targetPort: 5001
 12   selector:
 13     app: my-custom-web-service
 14   type: NodePort

웹 서버의 내부 포트인 5001번 포트 를 임의의 40001번 포트 를 이용하여 접근할 수 있도록 하였으며, 웹 서비스의 App Label을 연결하여, 서비스가 동작중인 Pods 에 접근하여 구성될 수 있도록 하였다.

결과적으로 Service 생성 후 동작 상태를 다음과 같이 대시보드에서 확인할 수 있었다.

이로서 간단하게 클러스터를 구축하고 서비스를 생성하는 것 까지 완료하였다. 이제 서비스가 정상 동작을 하는 것을 확인하기 위해, Cluster IP 로 할당된 10.109.253.251 로 접속을 해보고자 했다.

하지만 안타깝게도, 노출된 IP로 접근이 되지 않았다. 이와 관련하여 검색해 보니 minikube 를 이용하는 경우 minikube 의 노드 위에서 동작하기 때문에 Host PC에서 직접 접근이 되지 않는다.
(이 현상을 겪으며 확인해 보니 Host PC에서는 동작중인 Docker Container가 나타나지도 않았다)

따라서 minikube 노드에 SSH를 이용하여 접속한 후, 다음 이미지와 같이 curl 명령어를 통해 서비스에 주기적으로 요청을 보내니 원하는 것과 같이, 서로 다른 Pods 에 요청이 분산되며 결과가 나타나는 것을 확인할 수 있었다.

확인하고자 하는 내용은 마무리 했지만 컨테이너 구성까지 완료했는데 브라우저를 통한 확인을 하지 못해 찝찝한 마음에 약간의 서치를 추가로 해 보았더니, minikube 는 내부에서 동작중인 오브젝트에 대한 터널링을 지원하고 있었다. 다음과 같이 동작중인 쿠버네티스 서비스의 터널링을 진행할 수 있다.

이는 192.168.49.2 IP에서 동작하고 있는 minikube 노드의 서비스인 my-service에 대해, 해당 서비스가 클러스터 내부 포트 40001 번을 이용하고 있으며 외부에서 접근할 수 있는 30930 포트에 대해 터널링을 진행해준다.

결과적으로 Host PC의 127.0.0.1:52756 -> 192.168.49.2:30930 으로 터널링이 진행되었으며, 192.168.49.2:30930 -> 10.109.253.251:40001 로의 흐름을 통해 서비스에서 포드로 요청이 전달된다. 이후 클러스터 내부에서 트래픽이 분산되어, 각 Pods 에 요청이 전달된 후 응답이 다시 Host PC로 돌아오는 형태이다.

따라서 위와 같이 127.0.0.1:52756 에 웹 브라우저로 접속한 결과, my-deployment-5d6d8c576-2ndnx 포드에서 동작중인 컨테이너로 요청이 전달되었으며 웹 서비스가 요청에 대해 처리한 것을 확인할 수 있었다.

결론

쿠버네티스의 기본적인 오브젝트 개념에 대해 정리를 진행하며 간단한 실습을 진행해 보았다. Deployment나 Pods의 개념은 쿠버네티스 시스템을 구성하는 매우 기본적인 내용이기 때문에 정확히 개념을 이해하는 것이 중요하다고 생각하며, 추후 심화된 내용을 공부하는 데에도 필수적인 지식이 될 것이다. 또한 NodePort 를 이용하여 간단한 서비스를 구성해 보았는데, 실제 서비스에서는 보안적인 부분을 무시할 수 없기 때문에, 외부에 IP를 노출시키고 트래픽을 처리하는 방식은 이용되기 어렵다. 이 부분과 관련해서도 추후 포스팅을 통해 정리를 해보고자 한다.

0개의 댓글