12 Factor App (SaaS 서비스)

12 Factor App (SaaS 서비스)

SaaS(Software as a Service) 환경에 적합한 애플리케이션을 개발하기 위한 모범 사례를 정리한 것이 바로 "12-Factor App" 원칙이다. 이 원칙들은 애플리케이션을 더 관리하기 쉽고, 클라우드 플랫폼에서 효율적으로 실행되도록 설계하는 데 중점을 둔다. 2011년 Heroku의 개발자들에 의해 처음 소개되었으며, 아래와 같은 12가지 요소로 구성된다.

No alternative text description for this image
이미지 출처: https://blog.bytebytego.com/p/ep105-the-12-factor-app

1. 코드베이스(Codebase)

하나의 애플리케이션에 대해 하나의 코드베이스(codebase)를 가지고 버전으로 추적 관리해야 한다.

코드베이스는 Github 같은 코드 저장소라고 생각하면 된다.

애플리케이션과 코드베이스는 1:1 관계이다. 코드베이스는 컴파일되어 여러 단계(개발, 테스트, 운영 등)의 서버 환경으로 배포될 수 있지만, 소스 코드 저장소 내의 원천 소스는 하나를 가진다는 뜻이다.

2. 의존성(Dependencies)

프로그램에 사용되는 라이브러리들은 암묵적인 의존성이 발생하지 않아야 한다.

프로그램에 사용하는 라이브러리의 종속성은 명시적으로 선언하고, 선언된 라이브러리 이외의 종속성이 발생하여 오류를 유발하지 않아야 한다.

3. 설정(Configuration)

코드에서 사용하는 환경 설정 정보는 코드와 완전히 분리되어 관리해야 한다.

환경 설정 정보는 데이터베이스 연결 정보, 호스트명, 서버 실행 포트, Provider 연결 정보 등을 말한다.

구성 설정은 환경에 따라 변할 수 있으므로, 코드와 분리하여 관리해야 한다.

4. 백엔드 서비스(Backing Services)

데이터베이스나 메시징 시스템과 같은 백엔드 서비스를 애플리케이션에 연결된 리소스로 취급하고, 코드 변경 없이 교체할 수 있어야 한다.

5. 빌드, 릴리즈, 실행(Build, Release, Run)

애플리케이션 배포를 빌드(코드 컴파일), 릴리즈(빌드 + 설정), 실행(릴리즈 실행)의 엄격히 분리된 단계로 관리한다.

6. 프로세스(Processes)

애플리케이션을 무상태(stateless) 프로세스로 설계, 실행한다.

디스크나 메모리에 저장하는 쿠키나 세션 등을 가지지 않는 무상태(stateless)로 만든다. 상태 관리가 필요한(stateful) 데이터는 데이터베이스 혹은 캐시에 저장한다. 이렇게 높은 확장성을 갖게 한다.

7. 포트 바인딩(Port Binding)

한 서버 내에서 다양한 서비스가 독립적으로 수행될 수 있기 때문에 독립 포트를 바인딩하여 서비스로 제공한다.

8. 동시성(Concurrency)

12-Factor App의 동시성(Concurrency) 원칙은 애플리케이션이 다중 작업을 효율적으로 처리할 수 있도록 설계하는 것을 목표로 한다. 이는 애플리케이션이 다양한 형태의 작업 부하를 처리하기 위해 확장성을 제공하고, 시스템의 유연성과 안정성을 보장하는 중요한 원칙이다.

동시성 원칙의 핵심 개념

동시성 원칙에서는 애플리케이션이 다음과 같은 방법으로 동시성을 관리할 것을 권장한다.

  1. 프로세스 모델: 애플리케이션을 여러 개의 작은 프로세스로 분할하여 실행한다. 각 프로세스는 독립적이며, 특정 기능이나 작업을 수행한다.
  2. 확장성(Scalability): 필요한 경우 프로세스의 수를 늘리거나 줄임으로써 애플리케이션의 처리 능력을 조정할 수 있다.
  3. 무상태 프로세스(Stateless Processes): 각 프로세스는 무상태(stateless)여야 한다. 즉, 프로세스 자체는 상태를 저장하지 않고, 필요한 상태는 외부 저장소(데이터베이스, 캐시 등)에 저장한다.

9. 폐기 가능성(Disposability) – 빠른 시작과 그레이스풀 셧다운(graceful shutdown)을 통한 안정성 극대화

프로세스는 바로 시작하거나 종료될 수 있도록 한다.

그러면 빠른 배포(코드, 설정의 변경)와 탄력성 있는 확장(Scale-out)이 가능해지고, 결국 안정성 있는 production 배포를 할 수 있다.

프로세스는 시작 시간을 최소화하도록 해야 한다.

이상적으로, 프로세스는 실행 커맨드가 실행된 뒤 몇 초 만에 요청이나 작업을 받을 수 있도록 준비된다. 짧은 실행 시간은 릴리즈 작업과 확장이 더 빠르게 이루어질 수 있게 한다. 또한 프로세스 매니저가 필요에 따라 쉽게 프로세스를 새로운 머신으로 옮길 수 있기 때문에 안정성도 높아진다.

프로세스는 프로세스 매니저로부터 SIGTERM 신호를 받았을 때 graceful shutdown을 한다. 웹 프로세스의 graceful shutdown 과정에서는 서비스 포트의 수신을 중지하고, 현재 처리 중인 요청이 끝나길 기다린 뒤에 프로세스가 종료된다. 이 모델은 암묵적으로 HTTP 요청이 짧다는 가정을 깔고 있다. long polling의 경우에는 클라이언트가 연결이 끊긴 시점에 바로 다시 연결을 시도해야 한다.

worker 프로세스의 경우, graceful shutdown은 현재 처리 중인 작업을 작업 큐로 되돌리는 방법으로 구현된다. 예를 들어, RabbitMQ에서는 worker가 NACK을 메시지 큐로 보낼 수 있다. Lock-based 시스템들은 작업 레코드에 걸어놨던 lock을 확실하게 풀어놓을 필요가 있다.
즉 암묵적으로 모든 작업이 재진입성(reentrancy) 가능하다고 가정한다. 보통 트랜잭션을 설정하거나 요청을 멱등(idempotent)하게 함으로써 구현한다.

프로세스는 하드웨어 에러에 의한 갑작스러운 죽음에도 견고해야 한다.
드문 일이지만, 발생할 수 있다.
대책으로 Beanstalkd와 같은 견고한 큐잉 백엔드를 사용하는 것을 권장한다. 이러한 백엔드는 클라이언트가 접속이 끊기거나, 타임 아웃이 발생했을 때, 작업을 큐로 되돌린다. Twelve-Factor App은 예기치 못한, graceful 하지 않은 종료도 처리할 수 있도록 설계한다.

10. 개발/프로덕션 환경 일치(Dev/prod parity)

개발, 스테이징, 프로덕션 환경을 최대한 비슷하게 유지한다.

역사적으로, 개발 환경과 프로덕션은 큰 차이가 있었다.

  1. 개발 완료, 프로덕션 배포 간 시간 갭 차이: 개발자가 작업한 코드는 프로덕션에 반영되기까지 며칠, 몇 주, 때로는 몇 개월이 걸릴 수 있다.
  2. 담당자의 갭 차이: 개발자가 작성한 코드를 시스템 엔지니어가 배포한다.
  3. 툴의 차이: 프로덕션 배포는 아파치, MySQL, 리눅스를 사용하는데, 개발자는 Nginx, SQLite, OS X를 사용할 수 있다.

Twelve-Factor App은 개발 환경과 프로덕션 환경의 차이를 작게 유지하여 지속적인 배포가 가능하도록 디자인한다.

  1. 시간 갭 차이를 최소화: 개발자가 작성한 코드는 몇 시간, 심지어 몇 분 후에 배포한다.
  2. 담당자 갭 차이를 최소화: 코드를 작성한 개발자들이 배포와 프로덕션 모니터링에 깊게 관여한다.
  3. 툴의 차이를 최소화: 개발과 프로덕션 환경을 최대한 비슷하게 유지한다.

데이터베이스, 큐잉 시스템, 캐시와 같은 백엔드 서비스는 dev/prod 일치가 중요한 영역이다. 많은 언어들은 다양한 서비스에 대한 어댑터를 포함하고 간단하게 백엔드 서비스에 접근할 수 있는 라이브러리들을 제공한다. 예를 들어, Ruby/Rails는 ActiveRecord를 통해 MySQL, PostgreSQL, SQLite를 지원하고, Python/Django는 Celery를 통해 RabbitMQ, Beanstalkd, Redis를 지원한다.

프로덕션 환경에서는 고성능 서비스를 사용하지만, 로컬 개발 환경에서 가벼운 서비스를 사용하려고 할 수 있다. 예를 들어, 로컬에서는 SQLite를 사용하고 프로덕션에서는 PostgreSQL을 사용하거나, 개발 중에는 로컬 프로세스의 메모리를 캐싱용으로 사용하고 프로덕션에서는 Memcached를 사용하는 경우.

Twelve-Factor는 개발 환경과 프로덕션 환경에서 다른 서비스 사용을 지양한다. 어댑터가 서비스 간의 차이를 추상화해준다고 해도, 약간의 불일치가 개발 환경에서는 동작하고 프로덕션 환경에서는 오류를 일으킬 수 있다. 이러한 오류는 지속적인 배포(CD)를 방해한다.

11. 로그(Logs) – 로그를 이벤트 스트림으로 취급

로그를 이벤트 스트림으로 취급하고, 애플리케이션에서 로그 파일을 직접 관리하지 않는다. 이벤트 스트림을 관리한다.

로그 파일을 작성하거나, 관리하기 보다 이벤트 스트림을 관리하는 것이 중요하다.

이벤트 스트림은 Splunk 같은 로그 분석 시스템, Hadoop/Hive 같은 범용 데이터 보관소에 보내질 수 있다. 이러한 시스템은 장기간에 걸쳐 앱의 동작을 조사할 수 있는 강력함과 유연성을 제공한다.

12. Admin 프로세스(Admin processes)  - Admin을 실서비스와 함께 구현 및 배포

관리/유지보수 작업은 일회성 프로세스로 실행한다.

SaaS 서비스, 클라우드 네이티브 애플리케이션 개발자들에게 유용한 원칙이다.

참조링크

The Twelve-Factor App (한국어)
A methodology for building modern, scalable, maintainable software-as-a-service apps.
AWS 클라이드 네이티브 기반 Twelve Factor 앱 개발 방법 | Amazon Web Services
2012년 Heroku에서 일하던 개발자들은 클라우드 시대에 적합한 애플리케이션 개발과 배포 방법에 맞는 12가지 원칙(Twelve Factor)을 개념화 했습니다. 이와 비슷한 원칙 중 더 나은 코드를 위한 12가지 Joel Test라는 것도 유명하죠. Joel Test가 코드와 개발에 대한 것이라면 Twelve-Factor App 원칙의 주요 개발 동기는 아래와 같습니다. 애플리케이션 설정 자동화를 위한 절차(declarative)를 체계화 하여 신규 개발자의 학습 비용 […]
The Joel Test: 12 Steps to Better Code
Have you ever heard of SEMA? It’s a fairly esoteric system for measuring how good a software team is. No, wait! Don’t follow that link! It will take you about six years just to understa…