2024.12.19 - [개발 환경 및 운영] - Docker에서 Kubernetes로
지난 시간에는 Docker에서 Kubernetes로 가는 흐름에 대해 알아보았다. 이번 시간에는 Kubernetes의 주요 구성요소에 대해 알아보자
Pod: 컨테이너를 다루는 기본 단위
Kubernetes에서 컨테이너를 관리하는 가장 작은 단위
- Kubernetes가 Pod를 생성하면 해당 Pod는 클러스터 내부에서 고유한 IP를 가짐
- 하나의 Pod에는 하나 이상의 컨테이너가 포함될 수 있음 (pod ≠ container)
- 보통 하나의 애플리케이션 컨테이너를 하나의 Pod로 실행하지만, 필요에 따라 여러 컨테이너를 함께 실행할 수도 있다.
코드 예제
kubernetes는 yaml 파일로 상태를 선언해 리소스들을 관리한다. 세부적인 것은 다르지만 기본적으로 apiVersion, kind, metadata, spec이 최상위층에 존재한다.
- kind : 리소스의 종류. 여기서는 pod으로 정의함
- metadata : 이름과 라벨을 정의해서 리소스 식별하고 관리할 때 사용
- spec : 실제 동작 방식을 정의
apiVersion: v1
kind: Pod
metadata:
name: my-pod # pod 이름(같은 네임스페이스에서 중복 불가)
labels: # 리소스 분류하거나 검색할 때 사용되는 키-값 쌍
app: my-app # pod이 속한 애플리케이션 그룹
spec:
containers: # pod 내부에서 실행될 컨테이너들
- name: my-container
image: nginx
ports: # 컨테이너에서 열리는 네트워크 포트
- containerPort: 80 # 컨테이너 내부에서 사용할 네트워크 포트
그렇다면 왜 컨테이너 = pod 로 사용하지 않을까?
pod를 사용하는 이유는 리눅스 네임스페이스를 공유하는 여러 컨테이너들을 추상화된 집합으로 사용하기 위함이다. pod 안에 컨테이너가 여러개라면 이 컨테이너들은 동일한 네트워크 네임스페이스를 공유한다. 이게 무슨말인지 예시를 한번 보자.
아래는 두개의 컨테이너가 하나의 pod에서 실행되는 예제이다. 하나의 컨테이너는 nginx 웹 서버를 실행하고, 다른 컨테이너는 이를 localhost로 접근하는 클라이언트 역할을 한다.
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
spec:
containers:
- name: nginx-container # 첫 번째 컨테이너: Nginx 웹 서버 실행
image: nginx
ports:
- containerPort: 80
- name: busybox-container # 두 번째 컨테이너: 클라이언트 역할
image: busybox
command: ["sh", "-c", "while true; do wget -qO- <http://localhost:80>; sleep 5; done"]
busy-box 컨테이너에서 http://localhost:80으로 요청을 보내면, nginx 컨테이너의 80번 포트로 연결이 되어 nginx 서버의 응답이 출력된다. 이는 pod 내의 컨테이너들이 네트워크 네임스페이스를 공유하기 때문이다.
Pod을 사용하는 이유
- 네트워크 네임스페이스 공유 : Pod 내부의 컨테이너는 같은 IP 주소를 사용하며, localhost를 통해 서로 통신할 수 있다.
- 컨테이너 결합 : 하나의 컨테이너만으로는 충분하지 않은 애플리케이션을 실행할 때, 여러 컨테이너를 하나의 Pod에서 묶어 동작하게 한다. (예: Nginx 서버와 로그 수집기를 같은 Pod에서 실행) Pod 내부 컨테이너들은 하나의 워커 노드에서 함께 실행되고 자원을 공유한다.
pod은 하나의 완전한 애플리케이션이지만 컨테이너는 그렇지 않다. 여러개의 컨테이너가 하나의 완전한 애플리케이션으로 동작할 수 있도록 컨테이너를 한번더 추상화해 pod 이라는 단위로 관리하는 것이다.
ReplicaSet: Pod의 생애주기와 요청 분배를 위한 관리 도구
지정된 개수의 Pod가 항상 실행되도록 보장
Pod의 생애주기
단일 pod은 영구적이지 않고 휘발적인 특성을 가지고 있다. pod은 다음과 같은 이유로 삭제되거나 중단 될 수 있다.
- 클러스터 노드가 다운되거나 스케일 조정이 필요할 때
- pod 자체에 오류가 발생하거나 kubernetes가 새로운 pod을 배치할 때
- 사용자가 직접 pod 삭제할 때
pod은 이런 특성 때문에 항상 같은 상태과 개수를 유지하려면 추가적인 관리 도구인 ReplicaSet이 필요하다.
여러개의 pod이 필요한 이유
그렇다면 왜 pod은 여러개가 필요할까? 클라이언트의 요청이 많아질수록 하나의 pod이 모두 처리하기에 리소스가 부족해질 수 있다. 여러 pod을 생성해 클라이언트 요청을 분산처리하면 리소스를 효율적으로 사용하고, 특정 pod에 과부하가 걸리지 않도록 할 수 있다. 만약 특정 pod 하나만 문제가 생겼다면 문제가 없는 다른 pod에서 요청을 처리하는 식으로 애플리케이션의 가용성을 높일 수도 있다.
ReplicaSet의 역할
여러 pod을 수동으로 하나하나 생성하고 삭제하는 비효율적인 관리에서 벗어나게 해주는 것이 주요 역할이라고 할 수 있다.
- 지정된 상태의 pod을 항상 유지하도록 관리
- pod이 삭제되거나 문제 발생시 자동으로 새로운 pod을 생성해 복구
코드 예제
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicaset # ReplicaSet 이름
spec:
replicas: 3 # 유지할 Pod 개수 (항상 3개를 유지)
selector:
matchLabels:
app: nginx-app # 이 라벨을 가진 Pod를 관리
template: # 새 Pod를 생성할 때 사용할 템플릿
metadata:
labels:
app: nginx-app # 새로 생성되는 Pod에 부여할 라벨
spec:
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 80
ReplicaSet과 Deployment
ReplicaSet은 Pod의 개수를 유지하고 관리하는 데 초점이 맞춰져 있지만, Deployment는 ReplicaSet을 활용해 애플리케이션의 롤아웃, 롤백 등 배포 관리를 추가로 제공한다. 보통은 직접 ReplicaSet을 사용하기보다는 Deployment를 통해 관리하는 것이 일반적이다.
Deployment: ReplicaSet과 Pod 배포 관리
ReplicaSet과 Pod의 배포를 관리하며, 애플리케이션의 업데이트, 롤아웃, 롤백 등을 수행
Deployment는 ReplicaSet의 상위 오브젝트이기 때문에, Deployment를 생성하면 대응하는 ReplicaSet이 함께 생성되므로 pod과 ReplicaSet을 직접 생성할 필요가 없다. 실제로 아래 코드를 보면 ReplicaSet에서 kind만 변경되고 나머지 정의는 거의 비슷하다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-container
image: nginx
ports:
- containerPort: 80
Deployment의 역할
ReplicaSet 자체로도 Pod의 수를 유지하고 관리할 수 있지만, 애플리케이션 배포와 업데이트를 더욱 효율적으로 처리하기 위해 Deployment가 필요하다. Pod-ReplicaSet의 관계와 비슷하다고 생각하면 된다. 새로운 버전을 배포하려면 ReplicaSet 자체를 수정하거나 새로운 ReplicaSet을 수동으로 만들어야 하는데, Deployment를 사용해 ReplicaSet의 배포를 자동으로 관리한다.
- 무중단 배포(롤링 업데이트) : 기존 pod을 점진적으로 교체하면서 새로운 버전의 애플리케이션을 배포
- 리비전 관리 : ReplicaSet의 변경 상태를 Revision으로 저장해 업데이트 실패 시 쉽게 롤백 가능
- 배포 선언 : 원하는 상태를 선언해두면 Deployment가 이를 유지
롤링 업데이트 설정 예시
apiVersion: apps/v1
kind: Deployment
metadata:
name: rolling-update-deployment
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-container
image: nginx:1.21 # 새 버전의 이미지
strategy:
type: RollingUpdate # 롤링 업데이트 방식
rollingUpdate:
maxUnavailable: 1 # 최대 1개의 Pod는 중단 가능
maxSurge: 1 # 최대 1개의 새 Pod 추가 가능
배포 전략은 strategy 항목에 정의한다. 위 코드에서 새버전 이미지인 nginx:1.21으로 변경 후 kubectl apply -f deployment.yaml 명령어를 실행하면, Deployment는 새로운 ReplicaSet을 생성하고 롤링 업데이트를 진행한다.
롤링 업데이트의 흐름을 보면, [새로운 pod 먼저 생성 > 기존 pod중 일부 종료 > 업데이트 된 pod이 정상동작하면 > 다음 pod 업데이트..] 와 같은 식으로 모든 pod을 한번에 꺼버리지 않고 점진적으로 교체해 애플리케이션이 중단되지 않고 배포가 된다.
- maxUnavailable
- 업데이트 중 동시에 중단될 수 있는 Pod 수
- 이 값이 1이라면 > 3개의 pod이 실행중이라면 2개는 항상 가용상태로 유지됨
- maxSurge
- 업데이트 중 동시에 생성될 수 있는 새 Pod 수.
- 이 값이 1이라면 > 3개의 pod 실행중이라면 업데이트 중에 한번에 최대 4개의 pod이 실행 될 수 있음
위 두 값은 정수가 아니라 백분율(%)로도 표현 할 수 있으며, 전체 pod 개수에 따라 동적으로 계산되기 때문에 더 유연하게 배포 전략을 가져갈 수 있다.
이번 시간에는 Kubernetes의 주요 구성 요소인 Pod, ReplicaSet, Deployment 에 대해 알아봤다. 정리하자면 pod은 컨테이너를 추상화해서 관리, ReplicaSet은 그런 pod을 추상화해서 관리, Deployment는 그런 ReplicaSet을 추상화해서 관리. 라고 표현 할 수 있겠다. 이렇듯 Kubernetes는 추상화 레벨이 매우 높아 복잡한 시스템을 간단히 관리할 수 있도록 돕지만, 처음 접했을 때는 이게 다 무슨 소리인가 하며 매우 헷갈리고 어려울 수 있다. 가상화 기술과 Docker부터 시작해 차근차근 지식을 쌓고, 이해가 안 되더라도 반복적으로 들여다보고, 일단 yaml 예제부터 작성하며 학습하다 보면, 어느 순간 개념들이 하나씩 이해되는 순간이 찾아올 것이다. 그럼 다음 시간에는 service와 ingress에 대해 알아보겠다.
'개발 환경 및 운영 > Kubernetes' 카테고리의 다른 글
Kubernetes의 Persistent Volume과 Persistent Volume Claim (0) | 2025.01.25 |
---|---|
Kubernetes의 네트워크 리소스: Ingress (with ALB) (0) | 2025.01.07 |
Kubernetes의 서비스(Service) (1) | 2025.01.01 |
Docker에서 Kubernetes로 (1) | 2024.12.19 |
가상화의 이해: 하이퍼바이저에서 Docker까지 (2) | 2024.12.15 |