# 22장. 애플리케이션 구성 이 책 전반에 걸쳐 쿠버네티스 기반 애플리케이션의 구성 요소를 살펴봤다. - 프로그램을 컨테이너화 - 컨테이너를 파드로 배치 - 파드를 레플리카셋으로 복제 - 디플로이먼트를 통해 롤아웃 - 상태 저장과 실제 애플리케이션을 분산 시스템으로 배포 22장은 애플리케이션을 구성하는 컨피규레이션을 어떻게 배치, 공유, 관리, 업데이트할 수 있을까를 다룬다.
## 알아야 할 원칙 쿠버네티스에서 클라우드 네이티브 애플리케이션을 개발할 때 적용하는
신뢰성과 민첩성(agility)의 목표에 부합하는 구조를 설계하기 위한 원칙은 아래와 같다. - 신뢰성 있는 원천 데이터로서의 파일 시스템을 취급 - 변경에 대한 품질을 보장하기 위한 코드 리뷰 수행 - 단계적 roll forward 및 roll back을 위한 기능 플래그 사용
### 신뢰성 있는 원천 데이터로서의 파일 시스템 YAML 객체의 파일 시스템을 신뢰성 있는 원천 데이터(Source of Truth)로 보아야한다. 쿠버네티스 클러스터에 배포된 API 객체는 신뢰성 있는 파일 시스템 원천 데이터를 반영한 결과다. 이것이 올바른 과점인 이유는 아래와 같다. 1. 클러스터를 불변 인프라(immutable infrastructure)로 취급할 수 있다.
변경 가능한 인프라는 배포에 따라 동작이 달라질 수 있다. 2. 파일 시스템을 통해 여러 팀 구성원과 쉽게 협업할 수 있다.
소스 컨트롤 시스템을 통해 동시 편집에 따른 충돌을 해결할 수 있다.
### 코드 리뷰의 역할 코드 리뷰는 고품질의 안정적인 코드를 생산할 수 있는 모범 사례다. 대부분의 서비스 중단은 오타나 기타 간단한 실수와 같은 예기치 못한 상황으로 인해 발생한다. 두 사람 이상이 컨피규레이션 변경을 확인할 경우 오류 가능성이 크게 줄어든다. 원천 데이터 파일 집합에 병합할(merge) 모든 변경 사항을 리뷰해야 한다.
### 피처 게이트 feature gate는 새로운 기능이 추가되는 경우 개발이 완료되기 전까지 테스트할 수 있게 활성화/비활성화하는 데 사용하는 것이다. 제어는 feature flag를 통해 가능하다. ```c if (featureFlags.myFlag) { // 여기서부터 피처 구현체 작성 } ``` 피처 게이트 사용의 장점은 아래와 같다. 1. 기능을 제공할 준비가 되기 훨씬 전에운영 환경의 브랜치에 코드를 커밋할 수 있다.
오랜 시간 유지되는 브랜치 병합 시 발생할 수 있는 충돌을 피할 수 있다. 2. 새로운 기능 사용을 위해 컨피규레이션 변경만 수행하면 된다.
운영 환경에서 변경 사항을 명확하게 알 수 있으며, 문제 발생 시 롤백이 간단하다.
디버깅을 단순화할 수 있고 기능 비활성화 시 버그 수정 등이 포함된 최신 버전 코드 전체를 롤백할 필요가 없다.
## 소스 컨트롤에서의 애플리케이션 관리 파일 시스템에 파일을 실제로 배치하는 방법을 살펴본다. 파일 시스템의 계층적 디렉터리 구조와 소스 컨트롤 시스템의 태그와 브랜치 개념을 활용한다.
### 파일 시스템 레이아웃 애플리케이션을 특정한 구조로 체계화하려는 주된 이유는 의미 있는 구성 요소 또는 계층으로 나누기 위함이다. 파일 시스템의 계층적 디렉터리 구조를 통해 아래와 같이 활용할 수 있다. ```sh /frontend frontend-deployment.yaml frontend-service.yaml frontend-ingress.yaml /service-1 service-1-deployment.yaml service-1-service.yaml service-1-ingress.yaml /service-2 service-2-deployment.yaml service-2-service.yaml service-2-ingress.yaml ```
### 정기 버전 관리 장기적 릴리스 관리를 위해서는 배포의 예전 모습을 되돌아볼 수 있는 것이 유용하다. 따라서 여러 컨피규레이션의 버전을 동시에 저장하고 유지 관리하는 것이 편리하다. 이를 위한 두 가지 방식은 아래와 같다. 1. 태그, 브랜치, 소스 컨트롤의 기능을 사용 2. 파일을 복제 해 다른 개정판의 디렉터리를 사용
#### 브랜치와 태그를 사용한 버전 관리 브랜치와 태그를 사용해 컨피규레이션 리비전을 관리하는 경우 디렉터리 구조는 버전별 디렉터리를 따로 관리하지 않는다. 소스 컨트롤의 HEAD가 계속하며 변경된다. 릴리스 컨피규레이션을 업데이르하려면 HEAD에 변경 내용을 커밋하고, 새로운 브랜치 및 태그를 생성하면 된다. 원하는 변경 사항을 릴리스 브랜치로 체리픽(cherry-pick)하고 새로운 태그를 부여할 수도 있다. 롤백을 고려하면 hot fix와 같은 커밋은 필요한 모든 릴리스 태그에 체리픽을 해두어야한다. ```{figure} /images/kuar/ch22/cherry-pick.png :alt: image :width: 440px cherry pick ```
#### 디렉터리를 사용한 버전 관리 이 방법에서는 버전이 지정된 각 디플로이먼트가 자체 디렉터리 내에 존재한다. 모든 배포는 특정 리비전이나 태그가 아닌 동일 HEAD에서 발생한다. 새로운 릴리스를 생성할 때는 current 디렉터리를 복사해 업데이트한다. 버그 수정 사항이 반영될 때 pull request는 모든 릴리스 디렉터리에서 YAML 파일을 수정해 진행한다. ```sh /frontend v1/ frontend-deployment.yaml frontend-service.yaml frontend-ingress.yaml current/ frontend-deployment.yaml frontend-service.yaml frontend-ingress.yaml /service-1 v1/ service-1-deployment.yaml service-1-service.yaml service-1-ingress.yaml v2/ service-1-deployment.yaml service-1-service.yaml service-1-ingress.yaml current/ service-1-deployment.yaml service-1-service.yaml service-1-ingress.yaml ```
## 개발, 테스트, 배포를 위한 애플리케이션 구조화 분산 애플리케이션을 신속하게 변경 및 테스트하고 변경 사항을 안전하게 배포하기 위한 구조화를 알아본다.
### 목표 개발 및 테스트 목표는 아래와 같다. 1. 각 개발자가 애플리케이션의 새로운 기능을 쉽게 개발할 수 있어야 한다.
개발자는 자신의 환경에서 작업할 대 모든 서비스를 사용할 수 있어야 한다. 2. 배포 전에 애플리케이션을 쉽고 정확하게 테스트할 수 있어야 한다.
높은 신뢰성을 유지하면서 빠른 기능 롤아웃에 있어 필수적이다.
### 릴리스 진행 릴리스가 나가기 까지의 단계는 아래와 같이 구분할 수 있다.
HEAD
컨피규레이션의 시작점(최신 변경 사항)
Development
개발자가 기능을 빌드하는 목적
Staging
테스트를 위한 시작 지점. 문제가 발생하지 않으면 이 시작점은 변경되지 않는다.
Canary
실제 트래픽을 테스트해 발생할 수 있는 문제를 파악한다.
Release
현재 운영 환경에 릴리스되는 단계다.

#### 개발 태그 소개 개발 태그 워크플로에서는 메인 브랜치에 새로운 커밋이 발생하면 개발 태그의 포인터를 앞으로 이동시킨다. 주기적인 릴리스 과정에서 HEAD는 자동화된 통합 테스트를 통해 검증된다.
#### 단계를 리비전에 매핑 (버전, 단계)의 모든 조합은 추론하기 어렵다. 파일 시스템의 경우 심볼릭 링크를 사용해 각 릴리스 단계의 이름을 리비전에 매핑할 수 있다. ```sh frontend/ canary/ -> v2/ release/ -> v1/ v1/ frontend-deployment.yaml ``` 버전 관리의 경우 해당 버전과 동일한 리비전에 해당하는 태그를 추가하면 된다. ```{note} git은 동일 커밋에 2개 이상의 태그를 추가할 수 있다.
공식 가이드에 명시되어 있지는 않다. [#](https://git-scm.com/book/en/v2/Git-Basics-Tagging) ```
## 템플릿을 통한 애플리케이션 매개변수화 환경 및 릴리스 단계에 대한 곱집합을 유지 관리하기란 매우 어렵다. 매개변수화된 환경은 대부분의 컨피규레이션에 템플릿을 사용하며 제한된 매개변수 집합을 혼합해 최종 컨피규레이션을 생성한다. 대부분의 컨피규레이션은 공유 템플릿 내에 포함되며, 매개변수화는 범위가 제한되고 환경 간 차이를 쉽게 시각화하고자 소규모의 매개변수 파일로 유지 관리된다.
### 헬름과 템플릿을 통한 매개변수화 [헬름](https://helm.sh)을 사용해 컨피규레이션을 매개변수화할 수 있다. 헬름 템플릿 언어는 mustache 구문을 사용한다. ```yaml metadata: name: {{ .Release.Name }}-deployment ``` 매개변수 전달을 위해 다음과 같은 `values.yaml` 파일을 사용한다. ```yaml Release: Name: my-release ``` 최종적으로 아래와 같은 결과가 생성된다. ```yaml metadata: name: my-release-deployment ```
### 매개변수화를 위한 파일 시스템 레이아웃 파일 시스템에서 템플릿과 파라미터를 아래와 같이 포인터의 조합으로 나타낼 수 있다. ```sh frontend/ staging/ templates -> ../v2 staging-parameters.yaml production/ templates -> ../v1 production-parameters.yaml v1/ frontend-deployment.yaml frontend-service.yaml v2/ frontend-deployment.yaml frontend-service.yaml ``` 버전 제어를 수행할 경우 아래와 같다. ```sh frontend/ staging-parameters.yaml templates/ frontend-deployment.yaml ```
## 전 세계에 애플리케이션 배포 애플리케이션은 각기 다른 두 지역에서 수십 또는 수백으로 확장될 수 있다. 클라우드 환경에서는 전체 region에서 장애가 발생할 수 있어서 여러 지역에 배포 및 관리하는 것이 충분한 서비스 작동 시간을 달성할 수 있는 유일한 방법이다.
### 전 세계 배포를 위한 아키텍처 데이터 복제와 같은 복잡한 주제는 다루지 않는다. 파일 시스템에서 애플리케이션 컨피규레이션을 배치하는 방법을 살펴본다. 특정 지역의 컨피규레이션은 개념적으로 배포 수명주기 단계와 동일하다. 따라서 컨피규레이션에 여러 지역을 추가하는 것은 새로운 수명주기 단계를 추가하는 것과 동일하다. - Development - Staging - Canary - EastUS - WestUS - Europe - Asia 파일 시스템에서 이를 모델링하면 다음과 같다. ```sh frontend/ staging/ templates -> ../v3/ parameters.yaml eastus/ templates -> ../v1/ parameters.yaml westus/ templates -> ../v2/ parameters.yaml ``` 버전 제어와 태그를 사용하는 경우 아래와 같다. ```sh frontend/ staging-parameters.yaml eastus-parameters.yaml westus-parameters.yaml templates/ frontend-deployment.yaml ```
### 전 세계 배포 구현 애플리케이션이 여러 지역에 배포되는 주 목표는 매우 높은 신뢰성과 가동 시간(uptime)을 보장하기 위해서다. 새로운 버전의 소프트웨어 릴리스로 인한 중단이 서비스 다운타임의 주요 원인이다. 이와 같은 이유로 고가용성 시스템의 핵심은 변경 사항에 대한 영향도를 줄이는 것이다. 다양한 지역에 걸쳐 새로운 버전을 릴리스 할 때는 지역을 이동하기 전에 유효성 검증을 통해 신뢰성을 확보하는 것이 좋다. 전 세계에 걸친 소프트웨어 롤아웃은 워크플로와 유사하다. 최신 버전으로 검증 환경의 버전을 업데이트한 다음 모든 지역에 대한 롤아웃을 수행한다. 롤아웃 간 시간 간격을 결정하려면 소프트웨어에서 말하는 '연기가 발생하는 데까지 걸리는 시간(mean time to smoke)'을 고려해야 한다. 이는 새로운 릴리스가 특정 영역에 롤아웃된 후에 문제가 발견되는 데 걸리는 평균 시간을 의미한다. 따라서 다음 지역으로 이동하기 전에 에러의 발생 확률이 충분히 낮아질 수 있을 만큼의 시간 동안 기다려야한다. 평균의 두 배에서 세 배 정도의 대기 시간을 갖는 것이 일반적이지만 애플리케이션에 다라 매우 다양하다. 지역의 순서는 다양한 지역의 특성을 고려해야 한다. 트래픽이 많고 적거나, 특정 기능이 더 인기 있거나 없거나할 수 있다. 보통 트래픽이 적은 곳에 배포하고 그 후 많은 곳에 배포하여 피해를 최소화한다. 릴리스 일정을 진행할 때는 규모에 관계없이 릴리스마다 완벽하게 수행하는 것이 중요하다. 다른 문제를 해결하고자 릴리스를 빠르게 진행하거나 '안전'하다고 믿을 때 많은 다운타임이 발생한다.
### 전 세계 배포를 위한 대시보드와 모니터링 중대규모에서 흔히 발생하는 중요 문제 중 하나가 다른 버전의 애플리케이션이 다른 지역에 배포돼 있다는 점이다. 이를 주의 깊게 추적하지 않으면 다른 버전 관리로 인한 스노우플레이크 문제가 발생할 수 있다. 따라서 여러 버전의 애플리케이션이 배포될 때 발생하는 경고뿐만 아니라 어떤 지역에서 어떤 버전이 실행 중인지 한눈에 알 수 있는 대시보드를 개발해야한다. 가장 좋은 방법은 활성 버전의 수를 세 개 이하로 제한하는 것이다. 테스트, 롤아웃, 롤아웃으로 교체되는 세 버전이다.
## 요약 22장에서는 소프트웨어 버전, 배포 단계, 전 세계 지역에 대한 개념을 통해 쿠버네티스 애플리케이션을 관리하는 방법의 지침을 소개했다. 그리고 3가지 원칙인 파일 시스템에의 의존, 코드 리뷰, 피처 게이트 사용을 통한 점진적 기능 추가 및 제거를 설명했다. 배포를 위한 애플리케이션을 구성할 때는 향후 몇 년 동안 사용해야 할 프로세스를 설정하고 있음을 명시해야 한다. ```{figure} /images/kuar/ch22/bye.png :alt: image :width: 440px ```