소개글
본격적인 패턴 소개 이전에 클라우드 네이티브 애플리케이션을 지탱하는 쿠버네티스의 핵심 개념들을 설명한다. 구체적인 쿠버네티스 리소스를 통해 주로 쿠버네티스의 추상화 방식에 대해서 다루게 된다.
DDD
도메인 주도 설계(Domain Driven Design)는 이를 사용하는 사람들의 영역 또는 지식의 영역을 중심으로 하는 소프트웨어 개발 철학이다. 과거의 많은 소프트웨어 설계에서는 객체가 가져야 할 데이터에 초점을 두어 객체를 설계하였다(데이터 주도 설계, Data Driven Design). 이러한 방식은 객체 자신이 포함하는 데이터를 조작하는 데에 있어서는 좀 더 용이할지 모르나, 실제 비즈니스 레벨에서 다뤄져야 하는 내용들이 코드 상에 반영되지 못하고, 소프트웨어와 현실 간의 괴리가 발생하는 경우가 많았다. 또한 설계 시에 협력을 고려하지 않았기에, 이 후 수정이 발생하는 경우 많은 비효율이 발생한다. 해당 객체의 의미가 무엇이고 어디에 사용되는지 본인 조차 모르는 경우가 종종 발생한다.
DDD는 이러한 괴리를 해소하기는 데에 초점을 둔다. DDD에서는 해당 소프트웨어가 사용될 실제 비즈니스 영역에서부터 출발한 설계를 지향한다. 이때의 도메인은 실제 세계에서 사건이 발생하는 집합 정도로 생각할 수 있을 것 같다. 즉, DDD는 현실에서 출발한 여러 도메인들의 상호작용을 객체 지향적인 언어로 풀어낸다고 이해할 수 있을 거 같다.
바운디드 컨텍스트 (Domain Driven Design):
- DDD의 중심 패턴. 컨텍스트를 적절하게 식별 및 분리를 통해 마이크로서비스로 전환할 수 있다.
- OOP에서 하나의 객체에 넣을 기능의 범위를 고민하듯, 하나의 컨텍스트 내부에도 어느 정도의 기능을 넣을지 고민해야 한다.
- 앞서, 데이터 주도 설계가 객체들을 Public하게, 즉 누구나 접근 가능하게 열어 놓았다면 DDD에서는 Bounded Context를 통해 실제 해당 도메인 내에서만 객체들이 상호작용할 수 있도록 설계한다. 이를 서비스 관점에서 바라보게 되면 MSA를 의미하게 된다.
해당 교재에서는 가볍게 이러한 주제가 있다는 정도로 소개하고 넘어가지만, DDD는 MSA를 지탱하는 주요한 개념이기 향후 DDD를 본격적으로 공부하여 포스팅할 계획을 세우고 있다.
클라우드 네이티브의 단계
책에서는 현대 MSA 환경의 추상화 레벨을 다음과 같이 표현한다.
Level 0: 코드 레벨
변수와 메소드, 인스턴스화하기로 결정한 클래스 등을 의미한다.
Level 1: 도메인 주도 설계
실제 비즈니스에 최대한 가깝게 설계하도록 소프트웨어를 설계하는 일종의 방법론.
비즈니스 경계 (바운디드 컨텍스트), 트랜젝션 경계(애그리깃), 사용하기 쉬운 인터페이스, 풍부한 API 등이 고려된다.
Level 2: 마이크로서비스 아키텍처 방식
분산 애플리케이션 설계 원칙을 고려하여 소프트웨어 아키텍처를 구성한다.
스케일(scale out/up), 회복성(resiliency) 등이 충족된다.
Level 3: 쿠버네티스 패턴(컨테이너)
컨테이너를 통한 애플리케이션 패키징과 실행이 가능해짐에 따라, 대규모의 컨테이너 기반 마이크로서비스 자동화가 요구된다.
쿠버네티스는 컨테이너 오케스트레이션 도구이자 클라우드 네이티브 플랫폼으로서 컨테이너 환경에서의 자동화와 컨테이너를 다루기 위한 다양한 방법을 제시한다. 해당 교재의 주제이기도 하다.
분산 기본 요소
앞서 언급하였듯 MSA는 DDD로부터 출발한다. DDD가 객체를 도메인 별로 나누었다면, MSA는 DDD에서 객체 대신 서비스를 넣었다고 생각할 수 있겠다.
*로컬 기본 요소(in-process): Java 또는 JVM에서 제공하는 빌딩 블록 요소
*분산 기본 요소: 클라우드 네이티브 환경에서의 빌딩 블록 요소 (해당 교재에선 쿠버네티스와 같다고 고려)
개념 | 로컬 기본 요소 | 분산 기본 요소 |
캡슐화 동작 | 클래스 | 컨테이너 이미지 |
인스턴스화 동작 | 객체 | 컨테이너 |
재사용 단위 | .jar 파일 | 컨테이너 이미지 |
컴포지션(Composition) | 클래스 A가 클래스 B를 포함 | 사이드카 패턴 |
상속 | 클래스 A가 클래스 B를 확장 | 'FROM 부모 이미지' 로 만든 컨테이너 이미지 |
배포 단위 | .jar/.war/.ear | 파드 |
빌드 타임 / 런타임 격리 | 모듈, 패키지, 클래스 | 네임스페이스, 파드, 컨테이너 |
초기화 필요 조건 | 생성자 | 초기화 컨테이너 |
초기화 직후 트리고 | Init 메소드 | postStart |
삭제 직전 트리거 | Destroy 메소드 | preStop |
정리(Clenup) 절차 | finalize(), 셧다운 훅 | Defer 컨테이너 (아직 구현 X) |
비동기 & 병렬 실행 | ThreadPoolExcutor, ForkJoinPool | 잡 |
주기적 작업 | Timer, ScheduledExecutorService | 크론잡 |
백그라운드 작업 | 데몬 스레드 | 데몬셋 |
설정 관리 | System.getenv(), Properties | 컨피그맵, 시크릿 |
짚고 넘어가야할 것은
분산 기본 요소와 객체 지향 방식의 로컬 기본 요소는 직접적으로 비교하거나 대체가 일반적으로 불가능하다.
애초에 추상화 레벨이 다르기에 두 개념 간의 기본 요소가 직접적으로 겹친다고는 할 수 없다.
컨테이너와 파드
쿠버네티스를 배우기 시작하는 입장에서 컨테이너와 파드 간의 차이만큼 혼동되는 개념도 없다. 해당 파트는 본격적으로 쿠버네티스 패턴에 들어가기 이전에 쿠버네티스 기본 요소를 정확히 이해하자는 데에 목적을 둔다.
컨테이너는 다음의 특징을 갖는다.
- 쿠버네티스 기반 클라우드 네이티브 애플리케이션의 빌딩 블록 (패키징 및 격리를 제공하는 단위로 기능)
- 컨테이너 이미지: 인스턴스화 이전의 클래스와 유사하다.
- 클래스의 확장 및 수정이 이뤄지듯, 컨테이너 이미지의 버저닝(versioning)을 통한 확장과 수정이 가능하다.
- 하나의 문제를 해결하는 기능 단위이며 독립적인 릴리스 주기를 갖는다.
- 독립된 런타임과 배포를 제공한다.
- 대부분의 경우, 1 마이크로서비스 = 1 컨테이너 이미지 이다.
파드는 다음의 특징을 갖는다.
- 컨테이너 그룹의 수명주기를 관리하기 위한 분산 기본 요소
- 컨테이너 그룹의 스케줄링과 배포, 격리된 런타임에 대한 최소 단위
- 파드 냉부의 모든 컨테이너는 같은 호스트에 스케쥴링
- 파일 시스템, 네트워크 자원, 프로세스 네임스페이스 등을 공유
위의 이유로 인해 파드 내에서는 파일 시스템 / 로컬 호스트 / IPC 등을 통해 컨테이너 간의 상호작용이 가능해진다.
즉, 쿠버네티스 내에서 애플리케이션이 스케쥴링되는 최소 단위는 파드이고, 그 내부에서 실제 실행되는 이미지는 컨테이너라고 이해할 수 있다.
서비스
쿠버네티스를 이루는 가장 주요한 특징 중의 하나는 HA를 위해서 파드가 언제든지 죽고 다시 시작될 수 있다는 점이다. 때문에 기존의 하드웨어에서 이뤄지는 네트워킹과 쿠버네티스에서 이뤄지는 네트워킹은 달라야 한다. 이를 가능하게 해주는 것이 바로 쿠버네티스의 기본 리소스 중 하나인 서비스이다.
주요한 서비스 요소로는 Cluster IP / Node Port / Load Balancer 등이 있으며, 엄밀하게 말해서 앞서 설명한 서비스의 역할을 수행하는 자원은 이 중 Cluster IP이다. ClusterIP는 파드들이 클러스터 내부의 다른 리소스들과 통신할 수 있도록 해주는 가상의 클러스터 전용 IP다. 이 유형의 서비스는 <ClusterIP>로 들어온 클러스터 내부 트래픽을 해당 파드의 <파드IP>:<targetPort>로 넘겨주도록 동작하므로, 오직 클러스터 내부에서만 접근 가능하게 된다.
요약하자면 다음과 같다.
- 언제나 삭제되고 변경될 수 있다는 파드의 특성을 고려한 네트워킹 리소스
- 파드의 주소는 애플리케이션 실행 중에도 변경 가능하다.
- 예를 들어, 클러스터의 노드에 문제가 생기면, 파드는 정책에 따라 다른 노드로 옮겨간다. 스케줄링된 노드가 변경됨에 따라 파드의 IP 주소도 변경된다.
- 대신 서비스에서는 고정 불변한 IP 주소와 포트를 제공한다.
- 서비스는 실제 파드의 IP 주소에 접근하지 않고도, 파드와 연결을 유지할 수 있는 방법을 제공한다.
- 즉, 서비스의 가장 기본적인 역할은 특정 파드의 IP 주소와 포트로의 진입점 역할을 수행하는 것이다.
레이블 과 애노테이션
쿠버네티스를 사용해봤다면 레이블을 통해 클러스터 내에 배포를 해본 경험이 있을 것이다. 레이블 셀렉터와 레이블을 통해 쿠버네티스 리소스들이 어떤 도메인으로 묶이는지를 구분한다고 이해하는 것은 그리 어렵지 않다. 다만, 레이블과 상당히 유사하지만 다른 역할을 수행하는 애노테이션이라는 요소가 있다. 해당 섹션에서는 두 주제 간의 차이를 비교해보고자 한다.
먼저 레이블을 요약하자면 다음과 같다.
- Key: Value 쌍의 Map 형태로 구성됨
- 마이크로서비스로 넘어오면서, 하나의 애플리케이션 레벨로 수행하는 핵심 아티팩트 개념이 사라짐
- 그럼에도 특정 서비스들이 하나의 애플리케이션에 속함을 표현할 경우가 발생 -> 레이블의 활용
- 레이블을 통해 하나의 논리적 단위로 관리 가능, 객체 간의 매칭과 쿼리 시에 활용
- 스케쥴러에서는 요구사항에 맞는 노드에 파드를 배치하기 위해, 레이블을 통해 파드들을 분산시키기도하고, 단일 노드에 위치시키기도 함.
이어서 애노테이션을 요약하자면 다음과 같다.
- Key: Value 쌍의 Map 형태로 구성됨
- 사람보다는 기계를 위한 용도
- 검색 불가능한 메타데이터를 지정하는데 활용
- 주로 다양한 도구나 라이브러리와의 상호작용을 위해 객체에 추가적인 메타데이터를 추가하는 용도
즉 두 요소의 차이를 다음과 같이 이해할 수 있겠다.
- 특정 자원에 동작을 수행하거나 쿼리를 수행 -> Label
- 기계가 인식하는 메타에이터 추가 -> Annotation
네임 스페이스
쿠버네티스 클러스터를 논리적 자원 풀로 나누는 데 활용되는 쿠버네티스 기본 리소스이다. 사실 네임스페이스라는 개념은 쿠버네티스의 고유한 개념은 아니고, 리눅스의 cgroup을 통해 관리되는 네임스페이스로부터 출발한다. 이때 네임스페이스를 통해 격리된 환경들은 멀티 테넌시 환경으로 활용될 수 있다. 해당 클러스터 내의 네임스페이스의 이름은 겹치지 않아야 하며, 이는 노드나 PersistentVolume과 같은 기본 요소도 마찬가지이다.
이때, 주의해야할 점은 네임스페이스가 클러스터 자원에 대해서 논리적인 영역을 제공하긴 하지만, 하나의 자원이 다른 자원에 접근하는 것을 막을 수는 없다는 점이다. 파드의 IP를 무슨 이유에서인지 알고 있다면 이를 통해 내가 가진 프로필과 다른 네임스페이스에 대해서도 접근이 가능하다.
이후 2장에서 소개될 ResourceQuota를 통해 네임스페이스 당 자원 소모량을 제한할 수 있다.
'DevOps' 카테고리의 다른 글
[Kubeflow] pipeline (0) | 2023.04.07 |
---|---|
[Kubeflow] MLOps 개발환경 설정 (0) | 2023.04.07 |
error: parsing file '/var/lib/dpkg/updates/0001' near line 0: newline in field name `#padding' (0) | 2023.03.23 |
Could not get lock /var/lib/dpkg/lock-frontend (0) | 2023.03.23 |
sudo ubuntu-drivers autoinstall 오류 (0) | 2023.03.23 |