12 Factor App 이란?
SaaS 애플리케이션을 만들기 위한 방법론으로써 프로그래밍 언어에 비종속적이며 DB, Queue, Memory-cache 등과 조합할 수 있는 방법론입니다. 이 방법론은 시간이 지나면서 망가지는 소프트웨어 유지비용을 줄이는 방법에 집중해 이상적인 개발 방법을 찾고자 했습니다.
12 Factor App 의 요소들
1. 코드 베이스 (Code Base)
코드 베이스는 VCS(Version Control System)을 사용해 변화를 추적하고 코드를 저장하는 저장소를 의미합니다. (예: Git, SVN 등)
이 방법론에서는 코드 베이스-앱이 항상 1 대 1 관계를 맺어야한다고 하는데, 쉽게 말해 코드는 한 곳에서 개발/배포 되어야한다 입니다. 준수하기 어렵진 않은데, 아래와 같은 경우가 있을 순 있겠네요.
- 코드 베이스가 여러개인 경우
→ 앱이 아닌 분산 시스템으로 간주하고, 각각의 코드 베이스별로 앱으로서 12 Factors를 따릅니다. - 여러 앱이 동일한 코드를 공유하는 경우
→ 12 Factors를 위반하므로 공유되는 코드를 라이브러리화 하고 각 앱에 종속성을 주입합니다. - 앱 배포가 여러개인 경우(개발/알파/릴리즈 등)
→ 코드 베이스 자체는 동일하게 유지하며, Git branch 등으로 동일한 앱을 여러개로 배포할 수 있습니다.
2. 종속성 (Dependencies)
전체 시스템에 특정 패키지가 암묵적으로 존재하는 것에 절대 의존하지 않으며, 애플리케이션의 모든 종속성은 명시적으로 선언해 사용해야합니다. 대부분의 Java 프로젝트에서는 Gradle 이나 Maven을 이용해서 의존성을 관리할 수 있습니다.
게다가, Spring boot의 경우에는 내장 톰캣이나 jetty를 임베딩해 배포까지 가능합니다.
3. 설정 (Config)
설정은 동일한 앱의 배포 단계(개발/알파/릴리즈 등)에 따라 달라질 수 있는 모든 것들을 의미합니다. 아래는 그 예시입니다.
- 데이터베이스 접속 정보
- OAuth 또는 외부 서비스 인증 정보
- 호스트 네임에 따라 달라지는 값
- 서비스 포트 정보
이 정보들은 코드 상에 상수 형태라도 저장되면 안되며, 코드 베이스와 WAS 설정파일(예: JNDI 설정)에도 저장하지 않는것을 권장합니다.
12 Factor App 에서는 이 규약을 준수하기 위한 방법으로 환경변수를 사용하는 것을 제시하며, Spring Cloud를 이용하는 경우에는 Spring Cloud Config를 사용하는 것도 좋은 방법입니다.
4. 백엔드 서비스 (Backing services)
백엔드 서비스는 네트워크를 통해 이용하는 모든 서비스를 의미하며, 서드파티 서비스 또한 포함합니다.
- 데이터 베이스
- 메시지 큐
- SMTP 서비스
- 캐시 시스템
- Push 서비스
- Amazon S3
- Google Maps
- 기타 등등
12 Factor App 에서는 이 백엔드 서비스를 연결된 리소스로 취급하고, 자유롭게 연결 및 분리가 가능해야하고 코드 수정 없이 전환이 가능해야합니다. 예를 들어, 하나의 My-SQL DB는 하나의 리소스입니다. 그리고 애플리케이션 레이어에서 샤딩을 하는 두 개의 MySQL DB는 서로 다른 리소스로 구분합니다.
이 규약의 경우에는 리소스의 연결 정보를 Config로 지정해 준수할 수 있습니다.
예를 들어, 개발 단계와 배포 단계의 데이터베이스 연결 정보를 Config로 지정해 코드 수정 없이 배포 단계마다 적절한 데이터베이스에 연결하도록 할 수 있습니다.
5. 빌드, 릴리즈, 실행 (Build, release, run)
12 Factor App 에서는 코드 베이스를 빌드, 릴리즈, 실행 단계로 엄격히 분리합니다. 코드 변경은 반드시 빌드 단계에서만 이루어져야만 하며, 모든 릴리즈는 항상 유니크한 릴리즈 아이디를 가져야합니다. 또 릴리즈는 추가만 가능하며 한 번 만들어진 릴리즈는 변경될 수 없고, 이전 버전으로 롤백이 가능해야합니다.
- 빌드
- 코드 베이스를 빌드라는 실행 가능한 번들로 변환시키는 단계
- 새로운 코드가 배포될 때마다 개발자에 의해 시작
- 릴리즈
- 빌드 단계에서 만들어진 빌드와 배포의 현재 설정을 결합하는 단계
- 실행(런타임)
- 서버가 재부팅되거나 프로세스 매니저에 의해 실행
6. 프로세스 (Process)
애플리케이션은 무상태 프로세스로 실행되어야하고, 아무것도 공유하지 않아야합니다. 유지될 필요가 있는 모든 데이터는 벡엔드 서비스(예: 데이터베이스, Redis)에 저장되어야합니다.
SaaS 애플리케이션 자체가 Scale-out 이 가능하기 때문에 각 인스턴스가 메모리와 파일을 공유할 수 없고, 공유하더라도 메모리에 저장된 내용이 다른 프로세스에 의해 처리될 수 있으므로 로컬의 상태를 없애야합니다.
7. 포트 바인딩 (Port binding)
애플리케이션을 다른 애플리케이션에서 런타임 인젝션이 아닌 HTTP 서비스로 접근할 수 있도록 포트 바인딩합니다.
예를 들면, 각각의 마이크로 서비스 A가 B의 서비스를 사용해야하는 경우에 B의 데이터베이스 접속 정보와 계정, 권한을 A에게 부여해 B의 데이터베이스에 접속하는 것은 이 규약을 위반하는 것입니다.
이 규약을 준수하기 위해서는 A에서 B 서비스로 HTTP 요청을 통해 원하는 데이터를 읽고/쓰고/수정/삭제 해야합니다.
8. 동시성 (Concurrency)
애플리케이션은 Scale-out이 가능해야하며, 프로세스가 데몬 형태가 아니어야합니다. 앞선 프로세스 규약에서 애플리케이션이 무상태이고 아무것도 공유되지 않기 때문에 자유롭게 확장하고, 자유롭게 축소할 수 있겠습니다.
9. 폐기 가능 (Disposability)
12 Factor App 에서는 프로세스의 시작과 종료, 배포가 빈번하기 때문에 애플리케이션의 시작 시간과 종료 시간의 최소화가 중요합니다.
또한 종료에 있어서는 종료 시그널을 받고 나서 애플리케이션은 신규 요청은 받지 않고 기존 요청은 최대한 빠르게 처리한 이후 종료되어야합니다.(= graceful shutdown)
long polling의 경우에는 연결이 끊긴 시점에 바로 다시 연결을 시도해야하고, worker 프로세스의 경우에는 현재 처리중인 작업을 작업 큐로 되돌리고 종료해야합니다.
하드웨어 에러에 의한 갑작스런 다운에도 견고해야하며, Spring Cloud를 사용하는 경우에는 Spring Cloud Circuit Breaker를 통해 이러한 장애를 대비할 수 있겠습니다.
10. 개발/프로덕션 환경 일치 (Dev/prod parity)
개발 환경, 스테이징 환경, 프로덕션 환경을 최대한 비슷하게 유지하는 것을 의미합니다. 보통 이 3단계 사이에는 세 가지의 큰 차이점이 있는데 아래와 같습니다.
- 시간의 차이
→ 개발자가 작업한 코드는 프로덕션에 반영되기 까지 시간이 소요됩니다. - 담당자의 차이
→ 대부분의 경우 개발자가 작성한 코드를 시스템 엔지니어가 배포합니다. - 툴의 차이
→ 배포 환경과 개발자의 환경이 OS 부터 사용 제품, 툴에 있어 다를 수 있습니다.
12 Factor App은 개발 환경과 프로덕션 환경의 차이를 최대한 작게 유지해 지속적인 배포가 가능하도록 설계되었습니다.
전통적인 애플리케이션 | 12 Factor App | |
배포 간의 간격 | 몇 주 | 몇 시간 |
코드 작성자와 코드 배포자 | 다른 사람 | 같은 사람 |
개발 환경과 프로덕션 환경 | 불일치 | 최대한 유사하게 |
특히, 데이터베이스, 큐잉 시스템, 캐시와 같은 벡엔드 서비스는 두 환경의 일치가 가장 중요한 영역입니다.
하지만 동시에 개발자 입장에서는 프로덕션에서 사용하는 서비스보다 가벼운 환경을 구축해 개발하는 것을 원하는 경우가 많습니다. (예를 들어, 프로덕션 데이터베이스는 MySQL인데 개발자 로컬 데이터베이스는 H2를 사용하는 경우)
12 Factor App에서는 이것 또한 프로덕션 단계에서의 오류 가능성이 존재하므로 강력히 제한하며, 저도 사실 요즘은 Docker를 통해서 데이터베이스도 손쉽고 간단하게 구축이 가능하니 충분히 준수할만한 규약이라고 생각합니다.
11. 로그 (Logs)
로그는 모든 실행중인 프로세스와 서비스의 아웃풋 스트림으로부터 수집된 이벤트가 시간순으로 정렬된 스트림입니다. 때문에 애플리케이션은 아웃풋 스트림의 전달이나 저장에 절대 관여하지 않습니다.
SaaS 애플리케이션은 언제든 인스턴스가 삭제되고 생성될 수 있으므로 로그는 스트림으로 취급해 별도 저장소에 보관해야합니다.
12. 어드민 프로세스 (Admin processes)
어드민 또는 유지보수 작업을 일회성 프로세스(데이터베이스 마이그레이션, 일회성 스크립트 실행 등)로 실행해야 합니다.
References
'🏗️ ARCHITECTURE' 카테고리의 다른 글
Onion Architecture란? (0) | 2022.03.20 |
---|---|
Facade Pattern과 API Composition (0) | 2022.03.13 |
[도메인 주도 설계] 2부 : 모델 주도 설계의 기본 요소 (0) | 2022.03.06 |
[도메인 주도 설계] 1부 : 동작하는 도메인 모델 만들기 (0) | 2022.02.01 |
[나의 MSA 구축 일기] 처음 MSA를 구축해보며 겪었던 시행착오들 (4) | 2021.02.21 |