쿠버네티스와 쿠버네티스 오케스트레이션 이론

본 포스트는 「실무까지 한 번에 끝내는 DevOps를 위한 Docker & Kubernetes feat. AWS EKS 초격차 패키지 Online」 강의를 학습하며 정리한 내용을 다룹니다.
컨테이너 서비스
쿠버네티스를 본격적으로 배우기 전에 먼저 컨테이너 서비스가 무엇인지 이해할 필요가 있습니다. 컨테이너 서비스란 애플리케이션을 컨테이너 형태로 실행하고, 배포하고, 관리할 수 있도록 제공하는 서비스 또는 운영 방식을 의미합니다.
기존에는 애플리케이션을 서버에 직접 설치해서 실행하는 방식이 일반적이었습니다. 예를 들어 Node.js 애플리케이션을 실행하려면 서버에 Node.js를 설치하고, 필요한 라이브러리를 추가한 뒤 환경변수까지 직접 설정해야 했습니다.
반면 컨테이너를 사용하면 애플리케이션을 실행하는 데 필요한 환경과 구성 요소를 하나의 패키지처럼 묶을 수 있습니다. 이를 통해 실행 환경을 일관되게 유지하면서 보다 간편하게 애플리케이션을 배포하고 운영할 수 있습니다.
컨테이너 서비스를 사용하는 이유
1. 실행 환경의 차이를 줄일 수 있습니다
개발 환경에서는 정상적으로 동작하던 애플리케이션이 운영 서버에서는 제대로 동작하지 않는 경우가 있습니다.
예를 들어 개발자는 Node.js 20 버전을 사용했지만 운영 서버에는 Node.js 18 버전이 설치되어 있을 수 있습니다. 또는 동일한 라이브러리를 사용하더라도 설치된 버전이 서로 다를 수 있습니다.
컨테이너는 애플리케이션뿐만 아니라 실행에 필요한 환경까지 함께 묶어 관리하기 때문에 이러한 환경 차이로 인해 발생하는 문제를 줄일 수 있습니다.
2. 배포가 쉬워집니다
기존 방식에서는 서버마다 애플리케이션 실행 환경을 직접 구성해야 했습니다. 하지만 컨테이너를 사용하면 미리 만들어둔 이미지를 서버에서 실행하는 것만으로 동일한 환경을 구성할 수 있습니다.
예를 들어 Docker 이미지를 생성한 뒤 Docker Hub나 AWS ECR과 같은 이미지 저장소에 올려두고, 운영 서버에서는 해당 이미지를 가져와 실행할 수 있습니다.
3. 확장과 축소가 쉬워집니다
사용자가 많아져 트래픽이 증가하면 이를 처리하기 위해 애플리케이션 서버의 수를 늘려야 합니다.
컨테이너 환경에서는 동일한 애플리케이션 컨테이너를 여러 개 실행할 수 있기 때문에 필요에 따라 애플리케이션 인스턴스를 비교적 쉽게 늘리거나 줄일 수 있습니다.
4. 서버 자원을 효율적으로 사용할 수 있습니다
가상머신은 각각의 운영체제를 포함한 상태로 실행되기 때문에 상대적으로 많은 자원이 필요합니다.
반면 컨테이너는 호스트 운영체제의 커널을 공유하면서 애플리케이션 실행 환경만 서로 격리합니다. 따라서 가상머신보다 상대적으로 가볍고 빠르게 실행할 수 있으며, 하나의 서버에서 더 많은 애플리케이션을 운영할 수 있습니다.
쿠버네티스란?
이제 본격적으로 쿠버네티스에 대해 알아보겠습니다.
쿠버네티스(Kubernetes), 또는 K8s는 컨테이너화된 애플리케이션을 자동으로 배포하고, 확장하고, 복구하며 관리하기 위한 오픈소스 플랫폼입니다.
쿠버네티스는 단순히 컨테이너를 실행하는 도구가 아닙니다. 여러 서버를 하나의 클러스터로 구성하고, 그 위에서 수많은 컨테이너가 안정적으로 동작할 수 있도록 관리하는 역할을 합니다.
- Docker: 컨테이너를 만들고 실행하는 도구
- Kubernetes: 여러 컨테이너를 운영 환경에서 관리하는 플랫폼
쿠버네티스를 사용하는 이유
쿠버네티스를 사용하는 가장 큰 이유는 운영 환경에서 수많은 컨테이너를 안정적으로 관리하기 위해서입니다.
쿠버네티스에서는 개발자가 원하는 상태를 선언하면, 쿠버네티스가 실제 클러스터의 상태를 지속적으로 확인하면서 사용자가 선언한 상태와 일치하도록 조정합니다.
예를 들어 개발자가 다음과 같이 선언했다고 가정해보겠습니다.
"백엔드 API 컨테이너를 항상 3개 실행해줘."
그러면 쿠버네티스는 현재 실행 중인 컨테이너의 수를 지속적으로 확인합니다.
- if. 현재 3개 실행 중 → 그대로 유지
- if. 현재 2개 실행 중 → 1개 추가 실행
- if. 현재 4개 실행 중 → 1개 제거
이처럼 쿠버네티스는 사용자가 선언한 원하는 상태(Desired State)를 지속적으로 유지하려고 합니다. 이러한 관리 방식을 선언형 관리(Declarative Management)라고 부릅니다.
쿠버네티스의 핵심 개념
1. 클러스터
클러스터는 쿠버네티스가 관리하는 여러 서버의 묶음입니다. 하나의 쿠버네티스 클러스터는 크게 컨트롤 플레인(Control Plane)과 워커 노드(Worker Node)로 구성됩니다.
2. 컨트롤 플레인
컨트롤 플레인은 쿠버네티스 클러스터의 두뇌 역할을 합니다.
애플리케이션을 어떤 노드에 배치할지 결정하고, 현재 클러스터의 상태를 지속적으로 감시하며, 실제 상태가 사용자가 원하는 상태와 일치하도록 관리합니다.
주요 역할은 다음과 같습니다.
- 클러스터 상태 관리
- 애플리케이션 스케줄링
- API 요청 처리
- 컨테이너 및 Pod 상태 감시
- 장애 발생 시 자동 복구 제어
3. 워커 노드
워커 노드는 실제 애플리케이션 컨테이너가 실행되는 서버입니다.
예를 들어 백엔드 API, 프론트엔드 애플리케이션, 배치 작업 등의 실제 워크로드가 워커 노드 위에서 실행됩니다.
따라서 역할을 간단히 구분하면 다음과 같습니다.
- 컨트롤 플레인: 클러스터 관리 담당
- 워커 노드: 실제 애플리케이션 실행 담당
4. Pod
Pod는 쿠버네티스에서 배포할 수 있는 가장 작은 실행 단위입니다.
컨테이너 하나가 쿠버네티스에 직접 배포되는 것이 아니라, 컨테이너는 기본적으로 Pod라는 단위 안에서 실행됩니다.
- Pod = 컨테이너를 감싸는 쿠버네티스의 최소 실행 단위
일반적으로 하나의 Pod에는 하나의 주요 컨테이너가 실행됩니다.
Pod
└── Container하지만 필요에 따라 하나의 Pod 안에서 여러 컨테이너를 함께 실행할 수도 있습니다.
Pod
├── App Container
└── Sidecar Container예를 들어 애플리케이션 컨테이너와 함께 로그를 수집하는 Sidecar 컨테이너를 동일한 Pod 안에서 실행할 수 있습니다.
5. Deployment
Deployment는 Pod를 어떻게 생성하고 유지하며 배포할 것인지 정의하는 객체입니다.
예를 들어 Deployment에 다음과 같은 요구사항을 정의할 수 있습니다.
- nginx 컨테이너를 사용합니다.
- Pod를 항상 3개 유지합니다.
- 새로운 버전 배포 시 Pod를 순차적으로 교체합니다.
Deployment를 사용하면 원하는 수의 Pod를 지속적으로 유지할 수 있으며, 새로운 버전의 배포와 이전 버전으로의 롤백 역시 쉽게 관리할 수 있습니다.
6. Service
Pod는 언제든지 새롭게 생성되거나 삭제될 수 있습니다. 여기서 발생하는 문제 중 하나는 Pod가 새롭게 생성될 때마다 IP 주소가 바뀔 수 있다는 점입니다.
따라서 다른 애플리케이션이 특정 Pod의 IP를 직접 바라보도록 구성하면 안정적인 통신이 어렵습니다. 이를 해결하기 위해 쿠버네티스에서는 Service를 사용합니다.
Service는 여러 Pod 앞에 고정된 접근 지점을 제공합니다. 즉, 뒤에 있는 Pod가 교체되거나 IP가 변경되더라도 클라이언트는 Service를 통해 안정적으로 해당 애플리케이션에 접근할 수 있습니다.
7. Ingress
Ingress는 클러스터 외부에서 들어오는 HTTP/HTTPS 요청을 내부의 Service로 라우팅하기 위한 객체입니다.
예를 들어 다음과 같은 라우팅 규칙을 구성할 수 있습니다.
https://example.com/api→ backend-servicehttps://example.com/admin→ admin-servicehttps://example.com→ frontend-service
Service가 클러스터 내부에서 애플리케이션에 접근할 수 있는 안정적인 지점을 제공한다면, Ingress는 외부 트래픽을 적절한 내부 Service로 연결하는 입구 역할을 합니다.
8. ConfigMap과 Secret
애플리케이션을 실행하기 위해서는 다양한 환경 설정값이 필요합니다.
예를 들어 다음과 같은 값들이 있습니다.
- API_URL
- DATABASE_HOST
- LOG_LEVEL
이처럼 외부에 노출되어도 큰 문제가 없는 일반적인 설정값은 ConfigMap으로 관리할 수 있습니다.
반면 비밀번호, API 키, 토큰과 같이 상대적으로 민감한 값은 Secret으로 관리합니다.
- ConfigMap: 일반적인 설정값 관리
- Secret: 민감한 설정값 관리
이렇게 애플리케이션 코드나 이미지와 설정값을 분리하면 동일한 이미지를 사용하면서도 환경에 따라 서로 다른 설정을 적용할 수 있습니다.
컨테이너 오케스트레이션
앞에서 살펴본 쿠버네티스의 역할을 이해하려면 컨테이너 오케스트레이션(Container Orchestration)이라는 개념도 함께 알아둘 필요가 있습니다.
컨테이너 오케스트레이션이란 여러 개의 컨테이너를 자동으로 배포하고, 실행하고, 확장하고, 복구하며 관리하는 기술을 의미합니다.
실제 운영 환경에서는 일반적으로 컨테이너 하나만 실행하지 않습니다. 예를 들어 쇼핑몰 서비스를 운영한다고 가정하면 다음과 같이 다양한 컴포넌트가 필요할 수 있습니다.
- 프론트엔드 컨테이너
- 백엔드 API 컨테이너
- 인증 서버 컨테이너
- 결제 서버 컨테이너
- 상품 서버 컨테이너
- 주문 서버 컨테이너
- Redis 컨테이너
- 메시지 큐 컨테이너
컨테이너의 수가 많아질수록 사람이 직접 각각의 컨테이너를 배포하고 상태를 확인하며 장애에 대응하는 것은 점점 어려워집니다.
이러한 문제를 해결하기 위해 등장한 것이 컨테이너 오케스트레이션이며, 쿠버네티스는 대표적인 컨테이너 오케스트레이션 플랫폼입니다.
컨테이너 오케스트레이션이 필요한 이유

1. 컨테이너가 죽었을 때 자동 복구가 필요합니다
운영 중인 컨테이너는 애플리케이션 오류나 서버 문제 등으로 인해 비정상적으로 종료될 수 있습니다.
이때 관리자가 직접 서버에 접속해서 문제를 확인하고 컨테이너를 다시 실행해야 한다면 복구까지 시간이 오래 걸릴 수 있습니다.
컨테이너 오케스트레이션 도구는 컨테이너의 상태를 지속적으로 감시하다가 문제가 발생하면 필요한 컨테이너를 자동으로 다시 실행하거나 새롭게 생성합니다.
2. 트래픽에 따라 컨테이너 수를 조절해야 합니다
사용자가 많아져 트래픽이 증가하면 이를 처리하기 위해 더 많은 컨테이너가 필요합니다.
반대로 사용자가 줄어들었는데도 많은 컨테이너를 계속 실행하고 있다면 불필요한 서버 자원과 비용이 발생할 수 있습니다.
따라서 트래픽이나 리소스 사용량에 따라 컨테이너의 수를 자동으로 늘리거나 줄이는 기능이 필요하며, 이러한 확장과 축소 역시 오케스트레이션의 중요한 역할입니다.
3. 여러 서버에 컨테이너를 분산 배치해야 합니다
실제 운영 환경에서는 서버 한 대만 사용하는 경우보다 여러 서버를 하나의 클러스터로 구성하는 경우가 많습니다.
이때 각각의 컨테이너를 어떤 서버에서 실행할 것인지 결정해야 합니다.
예를 들어 다음과 같이 배치할 수 있습니다.
- 서버 A: 프론트엔드 컨테이너
- 서버 B: 백엔드 컨테이너
- 서버 C: 데이터 처리 컨테이너
하지만 컨테이너가 많아질수록 사람이 서버의 CPU와 메모리 사용량을 일일이 확인하면서 배치하는 것은 어렵습니다.
오케스트레이션 도구는 각 서버의 CPU, 메모리 등의 자원 상태와 애플리케이션의 요구사항을 고려하여 컨테이너를 적절한 서버에 배치합니다.
4. 무중단 배포가 필요합니다
서비스를 운영하는 동안에도 새로운 버전의 애플리케이션을 지속적으로 배포해야 합니다.
이때 기존 컨테이너를 모두 종료한 뒤 새로운 컨테이너를 실행하면 그 사이에 서비스가 일시적으로 중단될 수 있습니다.
오케스트레이션 도구는 기존 버전과 새로운 버전의 컨테이너를 순차적으로 교체하여 서비스 중단을 최소화하면서 배포할 수 있도록 도와줍니다.
기존 컨테이너 유지
→ 새 컨테이너 일부 실행
→ 정상 동작 확인
→ 기존 컨테이너 제거
→ 새로운 컨테이너로 순차 교체이러한 배포 방식을 Rolling Update라고 부릅니다.
쿠버네티스 오케스트레이션의 핵심 역할
앞에서 살펴본 내용을 바탕으로, 쿠버네티스가 컨테이너 오케스트레이션 과정에서 수행하는 핵심 역할을 정리해보겠습니다.
1. 자동 배포
쿠버네티스는 YAML 파일 등에 정의된 설정을 기반으로 애플리케이션을 배포할 수 있습니다.
예를 들어 다음과 같이 원하는 상태를 선언할 수 있습니다.
- nginx 이미지를 사용합니다.
- Pod를 3개 실행합니다.
- 80번 포트를 통해 서비스를 제공합니다.
이렇게 원하는 상태를 선언하면 쿠버네티스는 해당 상태를 만들기 위해 필요한 Pod와 리소스를 생성하고 관리합니다.
2. 자동 복구
Pod나 컨테이너에 문제가 발생하면 쿠버네티스는 이를 감지하고 필요한 조치를 수행합니다.
예를 들어 3개의 Pod가 항상 실행되어야 하는데 하나의 Pod가 사라졌다면, 쿠버네티스는 새로운 Pod를 생성해 다시 3개의 상태를 맞춥니다.
원하는 상태: Pod 3개
현재 상태: Pod 2개
쿠버네티스 동작: Pod 1개 추가 생성이처럼 쿠버네티스가 지속적으로 원하는 상태를 유지해주기 때문에 운영자가 모든 장애에 직접 대응하지 않아도 되며, 결과적으로 서비스의 안정성을 높일 수 있습니다.
3. 자동 확장
트래픽이 증가하면 이를 처리하기 위해 Pod의 수를 늘릴 수 있습니다.
Pod의 수를 직접 변경할 수도 있지만, CPU나 메모리 사용량 등의 지표를 기준으로 자동으로 확장하거나 축소하도록 설정하는 것도 가능합니다.
예를 들어 다음과 같습니다.
평상시: Pod 3개
트래픽 증가: Pod 10개
트래픽 감소: Pod 3개이러한 기능을 Horizontal Pod Autoscaler(HPA)라고 부릅니다.
4. 로드밸런싱
하나의 애플리케이션을 여러 개의 Pod로 실행한다면 들어오는 요청 역시 적절하게 분산되어야 합니다.
쿠버네티스의 Service는 여러 Pod를 하나의 접근 지점으로 묶고, 들어오는 트래픽을 대상 Pod로 전달합니다.
사용자 요청
↓
Service
↓ ↓ ↓
Pod A Pod B Pod C이를 통해 클라이언트가 각각의 Pod에 직접 접근할 필요가 없으며, 특정 Pod 하나에만 요청이 집중되는 문제도 줄일 수 있습니다.
5. 무중단 배포
쿠버네티스는 새로운 버전의 애플리케이션을 배포할 때 기존 Pod를 한 번에 모두 제거하지 않습니다.
대신 일부 Pod부터 새로운 버전으로 교체한 뒤 정상적으로 동작하는지 확인하면서 점진적으로 전체 Pod를 새로운 버전으로 변경할 수 있습니다.
v1 Pod 3개 실행
→ v2 Pod 1개 생성
→ v1 Pod 1개 제거
→ v2 Pod 추가 생성
→ 모든 Pod를 v2로 교체이러한 Rolling Update 방식을 활용하면 서비스 중단을 최소화하면서 새로운 버전을 배포할 수 있습니다.
6. 롤백
새롭게 배포한 버전에 문제가 발생하면 이전 버전으로 되돌릴 수 있습니다.
예를 들어 v2 버전을 배포했는데 심각한 오류가 발견되었다면 이전에 정상적으로 동작하던 v1 버전으로 다시 롤백할 수 있습니다.
v1 정상 운영
→ v2 배포
→ 오류 발생
→ v1으로 롤백새로운 버전의 배포 과정에서 문제가 발생할 수 있기 때문에 롤백은 실제 운영 환경에서 매우 중요한 기능입니다.
7. 서비스 디스커버리
쿠버네티스에서는 Pod가 새롭게 생성되거나 삭제되면서 IP 주소가 계속 변경될 수 있습니다. 따라서 다른 애플리케이션이 특정 Pod의 IP를 직접 사용하도록 구성하는 것은 적절하지 않습니다.
대신 쿠버네티스에서는 Service 이름을 이용해 다른 애플리케이션에 접근할 수 있습니다.
예를 들어 백엔드 애플리케이션이 데이터베이스 서비스에 접근해야 한다면 다음과 같은 이름을 사용할 수 있습니다.
database-service쿠버네티스 내부 DNS는 이 Service 이름을 실제 Service 주소로 변환해주고, Service는 다시 적절한 Pod로 요청을 전달합니다.
덕분에 Pod의 IP가 변경되더라도 애플리케이션에서는 동일한 Service 이름을 계속 사용할 수 있습니다.
8. 설정과 비밀 정보 관리
쿠버네티스는 ConfigMap과 Secret을 이용해 애플리케이션의 설정값을 코드나 컨테이너 이미지와 분리하여 관리할 수 있습니다.
예를 들어 동일한 애플리케이션 이미지를 사용하면서 환경에 따라 서로 다른 API 주소를 설정할 수 있습니다.
- 개발 환경:
API_URL=dev.example.com - 운영 환경:
API_URL=api.example.com
즉, 개발 환경과 운영 환경마다 별도의 이미지를 만들 필요 없이 동일한 이미지를 사용하고 설정값만 다르게 주입할 수 있습니다.
일반적인 설정값은 ConfigMap으로 관리하고, 비밀번호나 API 키와 같은 민감한 값은 Secret으로 분리하여 관리할 수 있습니다.