요약: 쿠버네티스 컨트롤 플레인은 모든 통신을 API Server를 중심으로 처리합니다. 이러한 Hub and Spoke 패턴은 데이터 일관성과 시스템 확장성을 확보합니다. 또한 사용자가 원하는 상태를 정의하면 쿠버네티스가 알아서 이를 유지하는 선언적 방식으로 운영되어 관리 부담을 줄여줍니다. 이러한 특징은 Watch 메커니즘으로 더욱 효과적으로 작동하며, 각 컴포넌트는 실시간으로 변경 사항에 대응하고 데이터 정합성을 유지하며 사용자가 원하는 상태를 빠르게 구현하도록 지원합니다.
💡 리뷰어 한줄평
hunter.lee 쿠버네티스를 사용하는 개발자라면 꼭 한 번은 읽어 봐야 하는 글입니다! 항상 복잡하고 어렵게만 느껴진 쿠버네티스였다면, 잭의 글을 통해 쉽게 이해할 수 있습니다. 자신 있게 쿠버네티스를 활용해 보세요!
dory.m 사장님, 여기 ReplicaSet 2개요! 인프라에 관심 많은 백엔드 개발자분들께 강력히 추천드리는 쿠버네티스 맛집 아티클이에요! 잭의 설명을 따라가다 보면 레스토랑에서 맛있는 파스타를 먹듯 즐겁게 쿠버네티스를 이해할 수 있게 됩니다.
시작하며
안녕하세요. 머니코어서비스팀에서 백엔드 개발을 담당하는 잭입니다. 개발자로서 애플리케이션 배포나 운영을 위해 쿠버네티스를 자주 접하지만, 내부에서 어떻게 동작하는지는 자세히 몰랐습니다. 그래서 종종 이런 궁금증이 들었습니다. “내 애플리케이션이 쿠버네티스에서 배포될 때 내부적으로 어떻게 동작하는 거지?”
이 궁금증을 해소하기 위해 쿠버네티스 내부를 알아가는 스터디를 진행했습니다. 스터디 덕분에 조금은 쿠버네티스와 친해졌고, 공부한 내용을 정리해 글로 작성했습니다. 쿠버네티스를 전문적으로 다루지 않는 백엔드 개발자이기에 너무 깊은 내용은 작성하지 않았습니다. 하지만 저와 비슷한 궁금증을 가진 분들에게는 큰 도움이 될 거라 생각합니다.
쿠버네티스의 핵심, 컨트롤 플레인의 2가지 특징
이미지 출처: How to deploy a .Net Core app on Azure Kubernetes
쿠버네티스의 핵심은 컨트롤 플레인이며, 마스터 노드라고도 불립니다. 컨트롤 플레인은 쿠버네티스의 모든 데이터를 관리하며 내부에 중요한 컴포넌트들이 존재합니다. 컨트롤 플레인은 중요한 2가지 특징이 있습니다. 지금부터는 컨트롤 플레인을 쿠버네티스로 명칭하겠습니다.
쿠버네티스 2가지 특징을 먼저 비유를 통해 알아보자
이해를 돕기 위해 비유를 통해서 쿠버네티스의 2가지 특징을 설명하겠습니다. 쿠버네티스에게 내 애플리케이션을 배포시키는 플로우를 레스토랑에서 주문하는 상황에 빗대보겠습니다. 레스토랑에는 전체적인 관리를 담당하는 점장과 요리사들이 있습니다. 이 레스토랑에서 모든 대화는 점장을 통해서만 할 수 있으며 요리사들끼리 대화를 할 수 없습니다. 그리고 손님이 주문한 메뉴는 무조건 완성한다는 서비스 정신도 투철합니다.
요리사들은 각자의 역할이 정해져 있습니다. 1번째 요리사는 조리만 담당합니다. 2번째 요리사는 마지막 가니싱을 담당합니다.
제가 파스타 2개를 먹고 싶어서 점장에게 주문서를 전달했습니다.
menu : pasta
count : 2
주문서를 받은 점장은 까먹지 않게 메모장에 주문을 기록합니다.
menu : pasta
count : 2
status : 주문 접수
이후 점장은 1번째 요리사에게 주문서가 들어왔다고 알려줍니다. 1번째 요리사는 자신의 역할인 파스타를 조리합니다. 이때 2번째 요리사는 자신의 역할이 아니니 대기 중입니다. 1번째 요리사가 조리가 끝나면 점장에게 알려줍니다. 점장은 주문서에 파스타의 상태를 변경합니다.
menu : pasta
count : 2
status : 조리 완료
이제 점장은 2번째 요리사에게 조리가 끝났다고 알려줍니다. 2번째 요리사는 파스타에 치즈나 파슬리를 뿌리고 점장에게 알려줍니다. 점장은 주문서에 파스타의 상태를 변경합니다.
menu : pasta
count : 2
status : 가니싱 완료
전체적인 흐름은 아래와 같습니다.
앞서 말씀드린 대로 이 레스토랑의 독특한 특징은 “점장을 통해서만 대화가 진행된다” 와 “레스토랑은 손님의 주문을 어떻게서든 완성한다” 입니다. 이 2가지 특징은 쿠버네티스가 가진 특징이기도 합니다.
1. “점장을 통해서만 대화가 진행된다”
요리사끼리 대화하며 요리를 진행하는 방식이 아닙니다. 점장을 통해서 주문서를 받고 자신이 역할을 수행할 단계인지 확인합니다. 자신의 역할이 마무리되면 점장에게 알려줍니다.
쿠버네티스에서는 점장을 Hub(허브)라는 용어로 요리사를 Spoke(스포크)라는 용어로 설명할 수 있습니다. Spoke들은 Hub을 통해서만 통신을 하며, 이를 Hub and Spoke 패턴이라고 부릅니다.
2. “레스토랑은 손님의 주문을 어떻게서든 완성한다”
손님은 원하는 메뉴를 주문만 할 뿐입니다. 그 주문을 완성하는 건 레스토랑의 역할입니다. 손님이 파스타 2개를 주문했다면 레스토랑은 정확히 2개를 요리해 제공해야 합니다. 만약 파스타 1개를 바닥에 쏟았더라도 레스토랑은 다시 요리하여 손님이 원하는 2개의 파스타를 완성해야 합니다.
쿠버네티스도 마찬가지로 이 동작 방식을 사용하며, 이를 선언적 동작 방식이라고 합니다. 사용자는 원하는 상태를 전달하고 쿠버네티스는 그 상태를 맞추기 위해 노력하는 방식입니다.
레스토랑 비유를 쿠버네티스와 매칭
지금까지 레스토랑 비유를 통해서 쿠버네티스의 2가지 특징을 간접적으로 이해할 수 있었습니다.
- “점장을 통해서만 대화가 진행된다” → Hub and Spoke 패턴
- “레스토랑은 손님의 주문을 어떻게서든 완성한다” → 선언적 동작 방식
이제 본격적으로 쿠버네티스의 2가지 특징을 자세히 설명하겠습니다.
1. 쿠버네티스의 Hub and Spoke 패턴
Hub and Spoke 패턴이 생소한 분들도 계실 텐데요. Hub and Spoke 패턴은 자전거 바퀴를 생각하시면 됩니다. 중앙에 Hub가 존재하고 각 Spoke는 Hub와 연결되어 있습니다. 각 Spoke는 Hub를 통해서만 연결이 유지됩니다.
이미지 출처: 3 SEO benefits to building hub and spoke pages | Smart Insights
앞서 설명한 레스토랑에서 점장은 Hub 역할을 수행하고, 요리사들은 Spoke 역할입니다. 이 패턴의 특징을 기억하면서 쿠버네티스는 Hub and Spoke 패턴을 어떻게 적용했는지 살펴보겠습니다.
이미지 출처: Monitoring Kubernetes Control Plane using KubeSphere
“점장을 통해서만 대화가 진행된다”는 레스토랑 비유에서 배운 쿠버네티스의 중요한 특징입니다. 쿠버네티스에서 점장 역할을 하는 Hub는 API Server입니다. API Server와 연결되어 있는 컴포넌트들은 Spoke 역할입니다. 이 Spoke들은 서로 직접 통신하지 않습니다. 항상 API Server와 통신을 하는 구조입니다.
레스토랑의 요리사들은 조리만 하거나 가니싱만 하거나 정해진 역할이 있었습니다. 마찬가지로 쿠버네티스에서도 API Server의 주변에 있는 Spoke들인 Controller Manager, Scheduler, Kubelet은 각자 맡은 역할이 존재합니다. 각 Spoke들의 역할에 대해서는 다음 편에서 자세히 설명하겠습니다.
레스토랑 비유와 쿠버네티스에서 Hub and Spoke 패턴이 동작하는 법
레스토랑 | 쿠버네티스 |
---|---|
손님이 점장에게 주문서 전달 | 사용자가 API Server에게 배포 등 원하는 것을 주문 |
주문서를 메모장에 기록하는 점장 | API Server는 메모장 역할을 하는 etcd 저장소에 주문서를 저장 |
주문서가 변경될 때마다 요리사들에게 전달 | 주문서 변경될 때마다 API Server는 주변 컴포넌트에게 전달 |
Hub and Spoke 패턴의 장점
쿠버네티스가 Hub and Spoke 패턴을 사용할 때 장점은 이렇습니다.
-
데이터 일관성: 쿠버네티스의 데이터를 보관하는 곳은 etcd라는 저장소 한 곳입니다. etcd는 API Server를 통해서만 데이터의 변경이 가능하기 때문에 데이터의 일관성이 유지될 수 있습니다.
-
확장성: 새 기능이 추가되어 새로운 Spoke를 추가하는 것이 편리합니다. Hub에만 연결하면 되므로 전체 구조의 변경 없이도 확장이 가능합니다
Hub and spoke 패턴 요약
- Hub and Spoke 패턴은 중앙에 Hub가 통신의 중심이 되며 각 Spoke는 Hub와 연결되어 있는 구조입니다.
- 각 Spoke는 정해진 역할이 있으며, 그 역할을 수행하는 시점은 Hub가 변경사항을 전달해 줄 때입니다.
2. 쿠버네티스의 선언적 동작 방식
“손님이 한 주문을 레스토랑은 어떻게 해서든 완성한다”는 레스토랑 비유에서 배웠던 중요한 특징 중 하나입니다. 손님은 레스토랑이 어떤 노력을 하는지는 알 필요 없습니다. 단지 내가 원했던 주문이 완성되느냐가 궁금할 뿐입니다.
만약, 파스타 2개를 주문했는데 요리 중에 파스타 1개를 바닥에 흘렸다면, 레스토랑은 다시 1개를 새롭게 만들겠죠. 주문서에는 파스타가 2개가 적혀있기 때문입니다.
쿠버네티스도 사용자가 원하는 상태를 달성하기 위해서 내부적으로 노력합니다. 가끔 내 팟이 수십 번 재시작하는 상황을 경험한 적 있지 않으신가요?
이미지 출처: Vault pod is CrashLoopBackOff and keeps restarting
이유는 바로 쿠버네티스가 사용자가 원하는 팟의 개수를 맞추기 위해 노력하기 때문입니다. 사용자가 팟을 2개 요청했는데 어떤 이슈로 1개만 실행 성공했다면, 쿠버네티스는 요청한 개수를 맞추기 위해 나머지 1개를 끊임없이 재시도합니다. 마치, 파스타 2개 주문이 들어왔는데 1개를 바닥에 흘렸다고 1개만 나갈 수 없고 새로 1개를 만드는 것처럼 말이죠.
선언적 동작 방식과 반대 개념: 명령적 동작 방식
더 나은 이해를 위해 반대되는 개념인 명령적 동작 방식을 설명해 보겠습니다. 명령적 동작 방식은 레스토랑에 파스타를 만들기 위한 명령을 손님이 직접 적어 제공하는 방식입니다. 마치 아래처럼 여러 명령이 담긴 레시피를 레스토랑에 전달합니다.
- 파스타면은 끓는 물에 10분 동안 삶는다
- 삶은 면을 이탈리아산 토마토소스에 섞는다.
- 등등..
반면에, 선언적 동작 방식에서 손님은 원하는 주문서를 레스토랑에 전달하고 원하는 결과를 기다릴 뿐이죠.
쿠버네티스에서 명령적 동작 방식을 적용한다면 내 애플리케이션을 언제 생성하고 삭제할지 직접 쿠버네티스에게 알려줘야 합니다. 사용자가 신경 써야 할 점이 많아집니다.
선언적 동작 방식과 명령적 동작 방식은 각기 장점이 존재합니다.
- 선언적 동작 방식에서 손님은 원하는 상태를 전달하고 편안하게 결과를 기다리면 됩니다.
- 반면, 명령적 동작 방식에서는 손님은 자신의 요리를 주도적으로 만들어서 먹을 수 있다는 점이 장점입니다.
물론 쿠버네티스는 명령적 동작 방식도 지원합니다. 다만, 선언적 동작 방식을 추천하고 있습니다. 그 이유는 사용자가 세세하게 신경 쓰지 않더라도 쿠버네티스 스스로 안정적으로 동작하기 때문입니다.
쿠버네티스에게 전달하는 주문서: 매니패스토 파일
사용자가 원하는 상태를 적는 파일을 매니패스토 파일이라고 합니다. 레스토랑의 주문서와 같은 역할입니다.
# my-app.yaml
kind: Deployment // 원하는 객체 종류
metadata:
name: my-app // 이름
spec:
replicas: 3 // 개수
사용자는 위와 같은 매니패스토 파일을 쿠버네티스에게 전달합니다. 마치 이렇게 말하는 거죠.
“나는 Deployment라는 객체를 원해. 이름은 my-app으로 할 거야. 그 개수는 3개로 해줘.”
사용자는 단지 원하는 상태를 전달하면 쿠버네티스는 내부적으로 그 상태를 맞추기 위해 노력합니다.
선언적 동작 방식 요약
- 쿠버네티스는 사용자가 원하는 상태를 달성하기 위해 노력한다.
- 쿠버네티스는 물론 명령적 동작 방식도 지원하지만 선언적 동작 방식을 추천한다.
Hub and Spoke 패턴과 선언적 구조를 효과적으로 지원하는 방법: Watch 메커니즘
레스토랑 비유에서 점장과 요리사들은 주문서에 변경이 있다면 빠르게 대화를 주고받아 대응을 해야합니다. 예를 들어, 손님이 파스타 개수를 2개에서 3개로 변경했다면 점장은 빠르게 요리사들에게 변경사항을 전달해야 합니다.
이와 유사하게 쿠버네티스에서도 매니패스토 파일의 변경이 있다면 API Server는 그 이벤트를 빠르게 주변 컴포넌트에게 전달해야 합니다. 이런 식으로 이벤트를 빠르게 전달하는 방식을 쿠버네티스에서는 watch 메커니즘이라고 합니다.
watch 메커니즘은 쿠버네티스의 Hub and Spoke 패턴과 선언적 구조를 지원하는 방법입니다. watch 메커니즘에 대해서 자세히 설명해 보겠습니다.
watch 메커니즘이란
watch 메커니즘은 단어의 의미대로 누군가 쿠버네티스의 상태 변화를 실시간으로 지켜보고 있다는 의미입니다. 여기서 누군가는 API Server와 연결되어 있는 컴포넌트들, Controller Manager와 Scheduler 등을 의미합니다.
watch 메커니즘은 컴포넌트가 API Server에 요청하여 특정 매니패스토의 변경 사항을 스트리밍 방식으로 수신할 수 있도록 합니다. 이후 API Server는 매니패스토의 생성, 수정, 삭제 등 모든 이벤트를 컴포넌트에 전달합니다
watch 메커니즘 구체적인 동작 방식
위 사진을 살펴보면 컴포넌트가 watch 요청을 할 때 resourceVersion이라는 값을 함께 보내고 있습니다. resourceVersion은 매니패스토의 특정 버전을 나타냅니다.
그래서 watch 요청할 때 resourceVersion을 함께 보낸다는 건 API Server에게 해당 버전 이후의 변경 사항만 보내달라는 의미입니다.
실제 watch 메커니즘의 예시를 살펴보겠습니다.
이해를 위해 Request와 Response를 일부 수정했습니다. 실제 동작 방식을 확인하고 싶다면 watch 메커니즘 공식문서를 확인해 주세요.
- Request
GET /api/v1/pods?resourceVersion=10245
위 watch 요청은 컴포넌트가 API Server에게 이렇게 말하는 겁니다. “Pod의 변경 사항을 10245 버전 이후로만 받고 싶어.”
이후 API Server는 변경 사항이 발생하면 아래 응답 형식으로 컴포넌트에게 스트리밍 방식으로 전달합니다.
- Response
200 OK
Transfer-Encoding: chunked
Content-Type: application/json
{
"type": "ADDED",
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...}
}
{
"type": "MODIFIED",
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "11020", ...}, ...}
}
watch 메커니즘의 장점
watch 메커니즘의 장점은 다음과 같습니다.
- 실시간 대응: API Server로부터 실시간으로 변경 사항을 수신하기 때문에 컴포넌트는 즉각적으로 대응할 수 있습니다.
- 데이터 정합성: 중간에 누락되거나 중복으로 온 변경사항이 있더라도 마지막 버전의 resouceVersion만 대응한다면 데이터의 정합성을 보장할 수 있습니다.
Watch 메커니즘과 Hub and Spoke 패턴과의 관계
Hub and Spoke 패턴은 “모든 통신은 API Server를 통해서 한다”는 의미입니다. 컨트롤플레인 내 모든 컴포넌트는 API Server에게 watch 요청을 보내 변경 사항을 알려달라고 말합니다. 그러면 API Server는 매니패스토의 변경사항이 발생하면 스트리밍 방식으로 컴포넌트에게 전달합니다.
-
API Server의 역할: API Server는 Hub로서 클러스터의 모든 상태 정보를 관리합니다. 컴포넌트의
watch
요청을 수신하며 API Server는 쿠버네티스 내 모든 매니패스토의 변경 사항을 전달한다. -
컴포넌트 역할: API Server에게 watch 요청을 보내 변경사항을 받을 준비를 합니다. 변경사항이 전달되면 각 컴포넌트가 해야 할 역할을 수행합니다.
Watch 메커니즘과 선언적 구조와의 관계
선언적 구조는 “쿠버네티스는 사용자가 원하는 상태를 무조건 맞추기 위해 노력한다”는 의미입니다. 이를 위해서 컴포넌트는 현재 쿠버네티스의 상태와 사용자가 원하는 상태가 일치하는지 수시로 체크해야 합니다.
-
상태 일치하는지 체크: 사용자는 매니페스트 파일을 통해 원하는 상태를 전달합니다. 컴포넌트는
watch
메커니즘을 통해 변경사항을 수시로 전달받고 이 목표 상태와 현재 상태 간의 차이를 확인합니다. -
상태 일치시키기 위해 노력: 현재 상태가 사용자의 원하는 상태와 일치하지 않을 경우, 컴포넌트는 자동으로 조치를 취하여 상태를 조정합니다.
요약
지금까지 쿠버네티스의 2가지 특징을 살펴봤습니다. 긴 설명이 있었지만 다시 한번 요약했습니다.
특징 | 동작방식 | 장점 |
---|---|---|
Hub and Spoke 패턴 | 컨트롤 플레인 내 통신은 API Server를 통해서 한다 | 데이터 일관성, 기능 확장성 |
선언적 동작 방식 | 쿠버네티스는 사용자가 원하는 상태를 무조건 맞추기 위해 노력한다 | 쿠버네티스 자동 관리 |
마치며
2가지 특징 외에도 다양한 특징을 가진 쿠버네티스입니다. 분량의 한계도 있고, 글의 콘셉트도 개발자가 궁금해서 찾아보는 쿠버네티스이기에 너무 깊게 다루진 않았습니다.
직접 구축이나 운영을 하지 않는 개발자들에게는 쿠버네티스는 블랙박스 같습니다. 이 블랙박스의 내부를 알아갈 때는 시간과 에너지가 많이 쓰이지만 하나씩 알아갈 때는 즐거움도 큽니다. 너무 깊게도 말고 너무 얕게도 말고 적당한 톤으로 작성할 수 있어 즐거웠습니다. 많은 분들에게 도움이 되길 바라겠습니다.
다음 글에서는 애플리케이션이 배포되는 상황에서 각 컴포넌트들이 어떻게 동작하는지 살펴보겠습니다.