본문 바로가기
Rev's/스크랩

12 Factor

by RevFactory 2021. 1. 23.

The Twelve-Factor App

SaaS (Software As A Service) 를 위한 방법론

목적

- 설정 자동화를 위한 절차를 체계화한다. -> 새로운 개발자가 프로젝트 참여하는데 드는 시간과 비용을 최소화한다.

- OS따라 달라지는 부분을 명확하게 하여 실행 환경 사이의 이식성을 극대화한다.

- 클라우드 플랫폼 배포에 적합하고, 서버와 시스템 관리가 필요없게 된다.

- 개발과 운영 환경 차이를 최소화 하여 지속적인 배포가 가능하다.

- 툴, 아키텍처, 개발 방식을 크게 바꾸지 않고 확장할 수 있다.

 

 

1. 코드 베이스

코드 베이스와 앱 사이에는 항상 1:1 관계가 성립해야 한다.

여러개 앱이 동일한 코드를 공유한다면 12-Factor 를 위반하는 것이다. 이를 해결하기 위해서는 공유 코드를 라이브러리화하고 해당 라이브러리를 Dependency Package로 관리해야 한다. 단, dev, stage, production 환경과 같이 하나의 코드베이스로 여러 배포는 가능하다.

 

2. 종송성

특정 패키지가 암묵적으로 존재하는 것을 허용하지 않는다.

종속성 선언 manifest를 이용하여 모든 종속성을 완전하고 엄격하게 선언하여 사용해야 한다. 개발자는 코드 베이스를 체크아웃 받고 빌드 명령어만 입력하면 응용 프로그램 실행하는데 필요한 모든 것을 설치 할 수 있어야 한다.

 

3. 설정

설정과 코드를 엄격하게 분리해야 한다.

여기서 말하는 설정의 정의는 데이터베이스 연결 정보, 서비스 인증 정보, 외부 연결을 위한 호스트 정보 등 배포(dev, stage, production)에 따라 달라질 수 있는 모든 것을 말한다. 코드베이스가 당장 오픈소스가 되더라도 가능한 상태로 설정이 분리되어 있어야 한다. 애플리케이션 설정을 포함하지 않음을 주의해야 한다.

이를 위해서 설정은 VCS에 등록되지 않는 설정 파일을 이용하는 것이다. 다만 설정파일이 여러 포멧으로 흩어지고 한 곳에서 확인하기 어려운 문제가 있다. 따라서 env와 같은 환경 변수로 설정을 저장하는 방법도 사용 가능하다.

(추가) Valult 나 Spring Cloud Config 와 같은 방안도 가능

 

4. 백엔드 서비스

백엔드 서비스를 연결된 리소스로 취급해야 하며, 로컬과 서드파티를 구별하지 않아야 한다.

백엔드 서비스란, 데이터 저장소(ex. MySQL), 메세지 큐잉서비스(ex. RabbitMQ), SMTP, 캐시 시스템 등 애플리케이션이 정상 동작 하는 동안 네트워크를 통해 이용하는 모든 서비스를 말한다. 또한 외부 SMTP 서비스나 지표수집 서비, 토리지 서비스 (ex. Amazon S3), API로 접근 가능한 서비스 (ex. Google Map) 등 서드파티에 의해 제공되는 서비스도 포함한다. 이때, 로컬 서비스와 서드파티 서비스를 구별하지 않는다. 이러한 리소스들은 첨부된 리소스(Attached Resource) 로 다루며, 이는 서로 느슨하게 결합된다는 점을 암시한다. 리소스에 문제가 발생하더라도 코드를 전혀 수정하지 않고 대체 리소스로 연결 가능해야 한다.

 

5. 빌드, 릴리즈, 실행

빌드, 릴리즈, 실행 단계를 엄격하게 서로 분리해야 한다.

- 빌드 단계 : 코드를 빌드하여 실행가능한 번들파일로 변환시키는 단계. 커밋된 코드 중 배포 프로세스에서 지정된 버전을 사용하며, 종석성을 가져와 바이너리와 Assets 들을 컴파일 한다.

- 릴리즈 단계 : 빌드 단계에서 만들어진 빌드와 배포의 현재 설정을 결합하는 단계. 완성된 릴리즈는 빌드 설정을 모두 포함하며, 실행 환경에서 바로 실행 가능하도록 준비한다.

- 실행 단계 : 실행 환경에서 앱을 실행하는 단계. 선택된 릴리즈에 대한 애플리케이션의 set을 시작한다.

배포 도구는 일반적으로 릴리즈 관리 도구를 제공하며, 특히 롤백 기능을 이용하여 문제가 발생하면 쉽고 빠르게 이전 버전으로 롤백 할 수 있어야 한다.

모든 릴리즈는 항상 유니크한 ID를 가져야 한다. 이때 릴리즈는 추가만 할 수 있으며, 한번 만들어진 릴리즈는 변경될수 없고 모든 변경은 새로운 릴리즈를 만들어야 한다.

빌드는 새로운 코드가 배포 될 때마다 개발자에 의해 시작되지만 실행 단계는 서버가 재부팅되거나 충돌이 발생하여 자동으로 재 실행 될 수 있다. 따라서 대응 가능한 개발자가 없는 한밤중에 문제가 발생하는 것을 방지하기 위해 실행 단계는 최대한 변화가 적어야 한다. 빌드 단계는 좀 더 복잡해져도 괜찮다.

 

 

6.  프로세스

앱은 하나 혹은 여러개의 무상태(stateless) 프로세스로 실행하며, 아무것도 공유하지 않아야 한다.

만일 유지될 필요가 있는 모든 데이터는 데이터베이스와 같은 안정된 벡엔드 서비스에 저장되어야 한다. 짧은 단일 트랜잭션내에서 캐시로 프로세스의 메모리 공간이나 파일시스템을 사용하는 것은 괜찮다. 절대로 메모리나 디스크에 캐된 내용이 미래의 요청이나 작업에서도 유효할 것이라고 가정해서는 안된다. 

웹 시스템 중 "Sticky Session"에 의존하는 것도 있는데 이는 유저의 세션 데이터를 앱의 프로세스 메모리에 캐싱하고, 같은 유저의 이후 요청도 같은 프로세스 전달될 것을 가정하는 것이다. 이러한 "Sticky Session"은 12-Factor에 위반되며, 절대로 사용하거나 의존해서는 안된다. 세션 상태 데이터는 Memcached나 Redis 처럼 유효기간을 제공하는 데이터 저장소에 저장하는 것이 적합하다.

Assets 패키징 도구는 컴파일된 Assets을 저장할 캐시로 파일 시스템을 사용하는데 이러한 컴파일은 런타임에 진행하는 것보다 빌드 단계에서 수행하는 것을 권장한다. 

 

 

7. 포트 바인딩

앱은 완전히 독립적이며, 포트바인딩을 사용해서 서비스를 제공해야 한다.

웹서버가 웹서비스를 만들기 위해 처리하는 실행환경에 대한 런터임 인젝션에 의존하지 않아야 한다. 이는 일반적으로 종속선 선언에 웹서버 라이브러리를 추가함으로써 구현된다. 이것들은 전적으로 유저 인터페이스 즉, 애플리케이션의 코드 내에서 처리된다. 실행 환경과 규약을 요청을 처리하기 위해 포트를 바인딩하는 것이다.

포트바인등을 사용한다는 것은 하나의 앱이 다른 앱을 위한 백엔드 서비스가 될 수 있다는 것을 의미한다는 것을 주목해야 한다. 백엔드 앱의 URL을 사용할 앱의 설정의 리소스 핸들로 추가하는 방식으로 앱이 다른 앱을 백엔드 서비스로 사용할 수 있다.

 

8. 동시성

프로세스 모델을 통한 확장으로 동시성을 보장해야 한다.

12-factor에서 프로세스드릉ㄴ First ClasS(최소 기본단위) 이다. 이 모델을 사용하면 애플리케이션의 작업을 적절한 프로세스 타입에 할당함으로써 다양한 작업 부하 처리가 가능하도록 설계할 수 있다. 런타임 VM 내부의 쓰레드나 EventMachin, Node.js 처럼 개별 프로세스가 내부적으로 동시 처리할 수 는 있겠지만 개별 VM이 너무 커지는 문제가 있다. 따라서 애플리케이션은 여러개의 물리 머신에서 돌아가는 여러개의 프로세스로 넓게 퍼질 수 있어야 한다. 이처럼 프로세스 모델은 수평으로 확장할때 빛을 발휘한다. 어떤 데이터도 공유하지 않고, 수평으로 배치하여, 간단하고 안정적으로 동시성을 달성할 수 있다. 프로세스의 타입과 각 타입별 프로세스 갯수의 배치를 프로세스 포메이션이라고 한다.

프로세스는 절대 데몬화해서는 안되며, PID 파일을 작성해서도 안된다. 대신 OS의 프로세스 관리자나 클라우드 플랫폼의 분산 프로세스 매니저를 통해 아웃풋 스트림을 관리하고 충돌이 발생한 프로세스에 대응하고 재시작과 종료를 처리해야 한다.

 

9. 폐기 기능 (Disposability)

빠른 시작과 Graceful shutdown 을 통해 안정성을 극대화 해야 한다.

12-factor App의 프로세스는 간단하게 폐기가 가능해야 한다. 즉, 프로세스는 바로 시작하거나 종료될 수 있어야 한다. 이러한 속성은 신축성 있는 확장과 코드나 설정의 변화를 빠르게 배포하는 것을 쉽게 하며, Produmction 배포를 안정성있게 해준다.

프로세스는 시작 시간을 최소화해야 한다. 이상적으로는 프로세스 실행 후 몇 초만에 요청이나 작업이 가능한 생태가 되어야 한다. 짧은 실행 시간은 릴리즈 작업과 확장(Scale Up)이 더 민첩하게 이뤄질 수 있게 하며, 또한 프로세스 매니저가 필요에 따라 쉽게 새로운 머신으로 프로세스를 옮길 수 있게 되므로 안정성도 높아지게 된다.

프로세스는 프로세스 매니저로부터 SIGTERM 신호를 받게되었을 때 Graceful Shutdown을 한다. 웹프로세스의 Graceful Shutdown 과정에서는 서비스 포트의 수신을 중지하여 새로운 요청을 거절하고, 현재 처리 중인 요청이 끝나길 기다린 뒤에 프로세스를 종료한다. 이 모델은 암묵적으로 HTTP 요청이 짧다는 가정을 깔고 있으며, long poling의 경우에는 클라이언트가 연결이 끊기더라도 바로 다시 연결을 시도하도록 해야 한다. worker 프로세스의 경우에는 현재 처리중인 작업을 큐로 되돌리는 방법으로 구현한다. Delayed Job 같은 Lock-based 시스템은 작업 레코드에 걸어놨던 lock을 확실하게 풀어놓을 필요가 있다. 그리고 프로세스는 하드웨어 에러에 의한 갑작스러운 중단에도 견고해야 한다. 

 

10. Dev / Production 일치

개발 환경과 Production 환경의 차이를 작게 유지하여 지속적인 배포가 가능하도록 설계해야 한다.

개발 환경과 Production 환경의 차이는 3가지 영역에 걸쳐 나타난다.

- 시간의 차이 : 개발자가 작업한 코드는 Production 에 반영되기까지 며칠, 몇주, 때로는 몇개월이 걸릴 수 있다.

- 담당자의 차이 : 개발자가 작성한 코드를 시스템 엔지니어가 배포한다.

- 툴의 차이 : 개발자는 Nginx, SQLite, OS X등을 사용할 수 있지만 Production 배포는 Apache, MySQL, Linux를 사용할 수 있다.

위에서 언급한 3가지 차이는 아래와 같이 대응한다.

- 배포의 간격 : 최대 몇주 -> 몇 시간

- 코드 작성자와 코드 배포자 : 다른 사람 -> 같은 사람

- 개발 환경과 Productio 환경 : 불일치 -> 최대한 유사하게 셋팅 (버전 포함)

이론적으로는 어댑터가 백엔드 서비스간의 차이를 추상화 해준다고 하더라도 백엔드 서비스 간의 약간의 불일치가 개발 환경과 스테이징 환경에서는 동작하고, Production 환경에서는 오류를 일으킬 가능성이 존재한다. 이러한 종류의 오류는 지속적인 배포를 방해하며, 애플리케이션 전체 주기를 보았을때 손해는 엄청나게 크다. 

 

11. 로그

로그와 같은 아웃풋 스트림의 전달이나 저장에 절대 관여하지 않아야 한다.

App은 로그파일을 작성하거나, 관리하려고 해서는 안된다. 대신 각 프로세스는 이벤트 스트림을 버퍼링 없이 stdout에 출력한다. 스테이징이나 production 배포에서는 각 프로세스의 스트림은 실행 환경에 의해 수집된 후, App의 모든 스트림과 병합하여 열람하거나 보관하기 위한 하나 이상의 최종 목적지로 전달한다. 이를 위해 오픈 소스 로그 라우터를 사용 할 수 있다. 

App의 이벤트 스트림은 파일로 보내지거나 터미널에서 실시간으로 보여질 수 있다. 가장 중요한 점은 스트림은 Splunk같은 로그 시스템이나 Hadoop과 같은 범용 데이터 보관소에 보내질 수 있다. 이러한 시스템은 장기간에 걸쳐 앱의 동작을 조사할 수 있는 강력함과 유사함을 제공한다.

 

12. Admin 프로세스

Admin/Maintenance 작업은 일회성 프로세스로 실행해야 한다.

프로세스 포메이션은 애플리케이션의 일반적인 기능들 (ex. Web Request의 처리)을 처리하기 위한 프로세스의 집합니다. 이와 별개로 개발자들은 종종 일회성 관리라 유지 보수 작업이 필요하다. 그 예는 아래와 같다.

- 데이터 베이스 마이그레이션 실행

- 임의의 코드를 실행하거나 라이브 데이터베이스에서 App의 모델을 조사하기 위해 콘솔을 실행. 

- 애플리케이션 저장소에 커밋된 일회성 스크립트의 실행

일회성 Admin 프로세스는 애플리케이션의 일반적인 오래 실행되는 프로세스들과 동일한 환경에서 실행되어야 한다. 일회성 Admin 프로세스들은 릴리즈를 기반으로 실행되며, 해당 릴리즈를 기반으로 돌아가는 모든 프로세스처럼 같은 코드베이스와 설정을 사용해야 한다. Admin 코드는 동기화 문제를 피하기 위해 애플리케이션 코드와 함께 배포되어야 한다.

모든 프로세스 타입들에는 동일한 종속성 분리 기술이 사용되어야 하며, 별도의 설치나 구성없이 REPL shell을 제공하는 언어를 강하게 선호하기도 한다. 이러한 점은 일회성 스크립트를 실행하기 쉽게 만들어준다.

 

 

출처 : 12factor.net/

'Rev's > 스크랩' 카테고리의 다른 글

Kubernetes 성숙도 모델  (0) 2021.01.28
프로젝트를 망치는 15가지 방법  (0) 2013.07.24
위대한 프로그래머  (0) 2013.04.25