"컨테이너가 많아졌는데, 누가 관리하죠?" - 쿠버네티스(Kubernetes) 입문

댓글 0
댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요!

댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요!
도커로 컨테이너를 하나 띄우는 건 어렵지 않다. docker run 한 줄이면 끝난다. 그런데 컨테이너가 10개, 100개로 늘어나면 얘기가 달라진다. 어느 서버에 올릴지, 몇 개를 띄울지, 하나가 죽으면 누가 다시 띄울지 — 직접 관리하기 시작하면 감당하기 어려워진다.
여기서 문제가 생긴다. 컨테이너는 만드는 것보다 운영하는 게 훨씬 어렵다. 서버가 죽어도 서비스는 살아 있어야 하고, 트래픽이 몰리면 자동으로 늘려야 하고, 배포할 때 서비스가 끊기면 안 된다. 쿠버네티스는 이 문제들을 해결하기 위해 만들어진 도구다.
이 글에서는 쿠버네티스가 왜 필요한지, 어떤 개념으로 구성되어 있는지, 그리고 어떻게 동작하는지 기초부터 정리한다.

도커만으로 서비스를 운영하는 상황을 상상해보자. 처음엔 서버 한 대에 컨테이너 몇 개를 올려서 쓸 수 있다. 하지만 트래픽이 늘고, 서비스가 쪼개지고, 서버가 여러 대로 늘어나기 시작하면 관리가 급격히 복잡해진다.
첫째, 어느 서버에 올릴지 직접 정해야 한다. 서버별로 남은 CPU와 메모리를 확인해서 적절한 서버에 수동으로 배포해야 한다. 서버가 20대라면 이 작업만 해도 상당한 시간이 걸린다.
둘째, 장애 복구가 수동이다. 컨테이너가 죽으면 누군가 알람을 받고, SSH로 접속해서 다시 띄워야 한다. 새벽에 장애가 나면 새벽에 일어나야 한다.
셋째, 스케일링이 어렵다. 트래픽이 몰리면 컨테이너를 더 띄워야 하는데, 어느 서버에 얼마나 띄울지 판단하고 실행하는 게 전부 사람 몫이다. 트래픽이 줄면 다시 줄여야 하는 것도 마찬가지다.
넷째, 무중단 배포가 까다롭다. 새 버전을 배포할 때 서비스가 끊기지 않도록 하려면 로드밸런서 설정과 컨테이너 교체를 세심하게 조율해야 한다. 스크립트로 짠다 해도 관리 포인트가 늘어난다.
쿠버네티스(Kubernetes, 줄여서 K8s)는 여러 서버에 걸쳐 컨테이너를 자동으로 배포하고 관리하는 도구다. 구글이 내부에서 쓰던 시스템을 오픈소스로 공개한 것이 시작이었고, 지금은 컨테이너 운영의 사실상 표준이 됐다.
핵심은 "내가 원하는 상태"를 선언하면, 쿠버네티스가 그 상태를 유지하기 위해 알아서 동작한다는 점이다. 예를 들어 "이 컨테이너를 3개 띄워둬라"라고 선언하면, 쿠버네티스는 항상 3개가 실행 중이도록 유지한다. 하나가 죽으면 자동으로 다시 띄우고, 서버가 다운되면 다른 서버에 옮겨 띄운다.
쿠버네티스는 선언형(Declarative) 방식으로 동작한다. "이렇게 해라"가 아니라 "이런 상태가 되어야 한다"를 YAML 파일로 정의한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:1.0
ports:
- containerPort: 3000
위 YAML은 "my-app:1.0 이미지를 쓰는 컨테이너가 3개 실행 중이어야 한다"는 선언이다. 쿠버네티스는 이 선언을 읽고, 현재 상태와 비교해서 부족하면 더 띄우고 넘치면 줄인다.

쿠버네티스는 클러스터(Cluster) 단위로 동작한다. 클러스터는 여러 대의 서버를 하나의 논리적인 단위로 묶은 것이다. 각 서버를 노드(Node) 라고 부른다.
노드 위에서 실제로 실행되는 최소 단위는 파드(Pod) 다. 도커를 쓸 때는 컨테이너가 최소 단위였지만, 쿠버네티스에서는 파드가 그 역할을 한다. 파드는 하나 이상의 컨테이너를 담는 껍데기라고 보면 된다.
| 구성 요소 | 역할 |
|---|---|
| 클러스터 | 전체 시스템. 여러 노드의 집합 |
| 노드 | 컨테이너가 실제로 실행되는 서버 |
| 파드 | 쿠버네티스의 최소 실행 단위. 컨테이너를 담음 |
노드는 역할에 따라 두 가지로 나뉜다.
개발자가 쿠버네티스에 명령을 내리면 컨트롤 플레인이 받아서 워커 노드에 작업을 분배한다. 사용자는 보통 컨트롤 플레인만 의식하면 된다.

쿠버네티스에는 수많은 리소스 종류가 있지만, 가장 먼저 이해해야 할 것은 세 가지다.
앞서 말한 것처럼 파드는 쿠버네티스의 최소 실행 단위다. 하나의 파드 안에는 보통 하나의 컨테이너가 들어가지만, 강하게 결합된 여러 컨테이너가 함께 들어가기도 한다.
중요한 건 파드는 일회용이라는 점이다. 죽으면 새로 만들어지고, 새로 만들어진 파드는 IP도 달라진다. 그래서 파드를 직접 관리하는 일은 거의 없다. 대신 Deployment를 쓴다.
Deployment는 파드를 몇 개 띄우고, 어떻게 업데이트할지 정의하는 리소스다. "이 이미지를 쓰는 파드를 3개 유지해라"를 선언하면, Deployment가 실제로 파드를 생성하고 관리한다.
Deployment가 있기 때문에 파드가 죽어도 걱정할 필요가 없다. Deployment가 알아서 새 파드를 띄운다. 새 버전으로 업데이트할 때도 Deployment가 롤링 업데이트(기존 파드를 하나씩 교체)를 수행한다.
파드는 IP가 자주 바뀌기 때문에 외부에서 파드에 직접 접근하기 어렵다. Service는 파드에 고정된 접근점을 제공하는 리소스다.
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 3000
type: ClusterIP
위 Service는 app: my-app 라벨을 가진 파드들에게 트래픽을 전달한다. 파드가 새로 생기거나 사라져도 Service의 주소는 바뀌지 않는다. 로드밸런서 역할도 같이 한다.

쿠버네티스를 쓰면 수동으로 하던 작업 중 상당 부분이 자동화된다.
파드가 죽으면 쿠버네티스가 알아서 다시 띄운다. 노드 자체가 다운되면 다른 노드에 파드를 옮겨 띄운다. 새벽에 장애가 나도 상당수는 쿠버네티스가 먼저 복구한다.
파드 개수를 늘리고 줄이는 게 명령 한 줄이면 된다.
kubectl scale deployment my-app --replicas=10
HPA(Horizontal Pod Autoscaler)를 설정하면 CPU 사용률에 따라 자동으로 스케일링도 된다.
새 버전을 배포할 때 기존 파드를 한 번에 전부 교체하지 않고, 하나씩 순차적으로 교체한다. 덕분에 배포 중에도 서비스가 끊기지 않는다. 문제가 생기면 이전 버전으로 롤백도 쉽다.
Service가 자동으로 파드들에 트래픽을 분산시킨다. 파드의 IP를 일일이 관리할 필요가 없다. DNS 이름으로 서비스에 접근하면 된다.
프로덕션 클러스터를 건드리기 전에 로컬에서 충분히 연습하는 게 좋다. Minikube, kind, Docker Desktop의 쿠버네티스 같은 도구를 쓰면 로컬에서 쿠버네티스 클러스터를 띄울 수 있다.
kubectl은 쿠버네티스를 조작하는 CLI다. 거의 모든 작업을 이걸로 한다.
kubectl get pods # 파드 목록 조회
kubectl apply -f app.yaml # YAML 파일 적용
kubectl logs my-pod # 파드 로그 확인
kubectl describe pod my-pod # 파드 상세 정보
이 몇 가지만 익숙해져도 기본적인 운영은 가능하다.
쿠버네티스는 학습 곡선이 가파르다. 개념이 많고, 리소스 종류도 많다. 처음부터 전부 이해하려고 하면 지친다. Pod → Deployment → Service 순서로 하나씩 익히면 충분하다. 나머지는 필요할 때 찾아서 배우면 된다.
모든 서비스에 쿠버네티스가 필요한 건 아니다. 트래픽이 적고 컨테이너가 몇 개 안 되는 서비스라면 도커 컴포즈나 ECS 같은 더 가벼운 도구가 나을 수 있다. 쿠버네티스는 규모가 커지고 자동화의 이점이 커질 때 빛을 발한다.
도커가 "컨테이너 하나를 어떻게 띄우느냐"의 문제를 해결했다면, 쿠버네티스는 "컨테이너 수십 개를 어떻게 운영하느냐" 의 문제를 해결한다. 선언형으로 원하는 상태를 정의하고, 나머지는 쿠버네티스가 알아서 유지해준다.
처음부터 전체 아키텍처를 이해하려 하지 말자. Minikube로 클러스터를 띄우고, 간단한 Deployment 하나를 kubectl apply로 올려보는 것부터 시작하면 된다. 파드 하나가 죽었는데 몇 초 뒤에 알아서 다시 살아나는 순간, 쿠버네티스가 왜 표준이 됐는지 체감할 수 있다.