1. 핵심 요약2. 소개 페이지 내용3. 12가지 요소 별 규칙/지침1. 코드베이스2. 종속성3. 설정4. 백엔드 서비스5. 빌드, 릴리즈, 실행6. 프로세스7. 포트 바인딩8. 동시성9. 일회성10. 개발 - 프로덕션 환경 일치11. 로그12. 운영 작업 4. 맺으며5. 출처
1. 핵심 요약
- 제목 “12 factor app”에서 “factor”는 “특정 요소에 기반한” 혹은 “요소를 포함한”이라는 의미로 해석할 수 있습니다. 즉 “12개의 요소에 기반한 애플리케이션”이라는 의미입니다.
- 12 factor app이란 클라우드 상에서 실행되는 서비스 개발 시 활용할 수 있는, 클라우드의 이점을 최대화함과 동시에 인프라에 대한 결합도를 최소화하는 지침(원칙)들의 집합입니다.
- 12가지 규칙의 주요 골자는 다섯 가지 입니다.
- 종속성과 설정을 명시적으로 정의해 설치,배포 과정을 쉽게 만들기
- 외부 인프라에 간접적으로 의존해 이식성을 높게 만들기
- 스케일 아웃으로 동시성을 높일 수 있게 만들기
2. 소개 페이지 내용
- 정의
- 서비스 형태의 웹앱(SaaS)을 만들기 위한 이상적인 개발 방법론
- 특정 개발 언어, 기반 도구와 무관하게 적용 가능합니다.
- 다음의 특징을 갖습니다:
- 설치 자동화를 하고, 선언적인 포맷을 활용
- OS와 깔끔한 계약을 맺어 실행 환경에 대해 최대한의 이식성을 제공
- 클라우드 배포에 적합하게 만들어 물리 서버의 필요성을 제거
- 개발 환경과 프로덕션 환경의 차이를 최소화하여 Continuous Deployment를 최대화
- 툴링, 설계, 개발 방식의 큰 변경 없이 스케일 업이 가능
- 근거
- 기여자들의 수백 개의 SaaS 개발 및 배포 경험과 heroku 플랫폼에서 간접적으로 목격한 수십만 개의 앱 개발, 운영, 확장 사례들에 기반
- 이상적인 앱 개발 방법론에 대해 애플리케이션의 점진적인 성장, 개발자들의 협업, 소프트웨어 부패를 막는데 초점을 맞춰 종합한 것
- ‣
- 소프트웨어 부패: 소프트웨어가 시간이 흐르면서 동작에 문제가 생기거나 사용이 불가능해지는 것
- 원인: 소프트웨어 자체가 부패해서 발생하는 것이 아닌 소프트웨어가 기반 환경에 맞게 업데이트되지 않기 때문에 발생
- 해결책: 앱과 플랫폼 간의 역할을 명확히 분리하고, 앱과 플랫폼 간의 계약 형태가 바뀌지 않도록 함
- 동기
- 개발 과정에서 발견한 일부 구조적인 문제를 공론화하고, 이 문제들을 논의하는데 필요한 언어, 포괄적이고 개념적인 해결책들을 제시
- 대상 독자
- 서비스를 개발하는 개발자
- 서비스를 운영하는 운영 엔지니어
3. 12가지 요소 별 규칙/지침
실제 문서 내용에서 확장성과 이식성에 관련된 내용만을 추출하였습니다.
내용은 제가 이해한대로 작성한 것이라 실제 내용과 다를 수 있습니다.
1. 코드베이스
- 앱은 실행 바이너리를 하나만 가져야 한다.
- 만약 실행 바이너리가 여러 개라면 이는 별도의 12-factor-app을 준수하도록 구성해야 한다.
- 여러 앱 간에 코드를 공유하는 경우 라이브러리로 만들어 의존성 관리자에 포함시켜야 한다.
2. 종속성
- 모든 의존성은 의존성 관리자를 통해 명시적으로 설치하고 의존한다.
- 의존성 관리자의 설치 명령어 외에 별도의 명령어 없이 설치가 돼야 한다.
- curl과 같이 의존 대상이 대부분에 시스템에 존재하더라도, 암묵적으로 의존하지 않는다.
- 필요한 모든 외부 의존성은 의존성 관리자로 관리한다.
3. 설정
- 배포 환경(dev,staging,prod)마다 달라질 수 있는 모든 값이 설정에 포함됩니다.
- (ex) hostname, 외부 api url, 인증 정보 등
- 코드 내에 어떠한 인증 정보도 없어야 합니다.
- 모든 설정 값은 OS 표준인 환경 변수로 관리합니다.
4. 백엔드 서비스
- 백엔드 서비스 = 네트워크를 거쳐 이용하는 의존성
- (ex) DB, Message Queue, SMTP 등
- 애플리케이션은 백엔드 서비스의 구현에 대해 모르며, 설정에서 주어진 값으로 연결합니다.
- 코드 수정 없이 self-hosted, managed 서비스 간 전환이 가능해야 합니다.
- (ex) local MySQL ↔ AWS RDS
5. 빌드, 릴리즈, 실행
- 세 단계에 대한 정의는 아래와 같습니다.
- 빌드 = 컴파일이 완료되었고 추가 설치가 필요 없는 상태
- 릴리즈 = 빌드와 배포 대상 환경의 설정이 결합된 상태. 바로 실행 가능.
- 실행 = 릴리즈가 실행된 상태.
- 각 단계의 결과물은 변경될 수 없으므로, 코드 수정이 필요하면 신규 빌드를 생성해야 합니다.
- 릴리즈는 immutable, 변경 불가능하며 고유한 식별자가 있어야 합니다.
- 실행 단계는 최대한 단순해서 문제 발생 가능성을 최소화해야 합니다.
6. 프로세스
- 프로세스는 무상태로 설계되어 아무 것도 공유하지 않아야 합니다.
- (ex) 전체 프로세스의 중간 결과물을 로컬 파일, 메모리에 보관 X
- (ex) sticky session을 사용해 프로세스의 local session을 사용 X
- 모든 공유 상태는 백엔드 서비스에 저장돼야 합니다.
- 앱은 필요하다면 여러 개의 프로세스로 실행됩니다.
- 일련의 요청이 서로 다른 프로세스에서 처리될 수 있어야 합니다.
- 일련의 요청이 재배포를 거치는 중에도 처리될 수 있어야 합니다.
7. 포트 바인딩
- (2. 종속성)의 내용에 포함된다고 봐도 되는 내용입니다.
- 앱 스스로 서비스에 진입하는 포트를 바인딩해야 합니다.
- 실행 환경에서 외부 웹 서버를 주입 받는 게 아니라 애플리케이션의 소스 코드 수준에 통합돼야 합니다.
- (ex) war를 Tomcat에 올려서 사용 vs Spring Boot에 내장된 Tomcat 사용
8. 동시성
- 앱은 프로세스 단위의 수평 확장을 통해 간단하고 안정적으로 처리량을 조절할 수 있어야 합니다.
- 큰 프로세스이면 수직 확장에 가까워지기 때문에, 프로세스는 작은 단위여야 합니다.
- 앱 프로세스는 별도의 관리 프로세스에 의해 관리될 수 있도록 시그널을 적절히 처리해야 합니다.
- 앱 프로세스는 확장성 있는 로그 수집을 구성하기 위해 로그를 stdout 출력으로 제공해야 합니다.
- 참고: ‣
- (ex) procfile - “프로세스 포메이션”을 지정합니다. 프로세스의 타입과 개수를 지정합니다.
web: node server.js worker: node worker.js
-c
는 개수를 의미)foreman export upstart /etc/init -c web=2 -c worker=4 -c clock=1
9. 일회성
- 배포와 스케일 아웃이 쉽고 빠르기 위해서 프로세스는 이상적으로 몇 초 이내에 시작되고 종료될 수 있어야 합니다.
- 프로세스는 SIGTERM 시그널을 받았을 때 graceful shutdown을 수행합니다.
- graceful shutdown?
- (api) 신규 요청 수신 중단, 기존 요청 처리 완료 대기 후 종료
- (worker) 진행 중이던 작업이 이후 재실행될 수 있도록 처리 후 종료
- (ex) 처리 중이던 메시지를 MQ로 반환하고 종료
- (ex) Worker가 작업을 멱등적으로 처리하도록 작성
- (ex) 이미 수행된 작업은 실행하지 않게 - 이미 전송된 메일은 무시, 항상 덮어쓰기 등
- 프로세스는 시그널 없는 강제 종료도 적절히 처리할 수 있어야 합니다.
- (ex) 일부 MQ는 worker과의 접속이 끊기거나, 타임 아웃이 발생했을 때, 작업을 큐로 자동으로 되돌림
- 참고: ‣
10. 개발 - 프로덕션 환경 일치
- dev, production 배포 환경의 차이를 최소화하여 몇 분 ~ 몇 시간 내에 배포될 수 있게 합니다.
- dev 환경에서도 production에서 사용하는 도구의 최대한 같은 종류, 같은 버전을 구축해서 사용합니다.
- (ex) 개발 환경에서 더 가벼운 SQLite, 셀프 in-memory DB 사용 X
- (ex) production 환경과 동일한 PostgreSQL, Memcached 사용 O
- 백엔드 서비스 간의 약간의 불일치로 인해 개발, 스테이징에서만 정상 동작할 수 있습니다.
11. 로그
- 로그는 앱에서 직접 처리하지 않고 실행 환경에서 처리합니다. 앱은 로그를 단순히 stdout으로 출력합니다.
12. 운영 작업
- 일회성 작업을 실행하는 경우에는 별도의 진입점을 두고 “프로세스 포메이션”으로 관리해야 합니다.
- 일회성 프로세스 역시 동일 코드베이스에서 관리하며, 릴리즈에 포함되어서 릴리즈의 종속성과 설정에 의존해야 합니다.
- (ex) DB 마이그레이션, 일회성 스크립트 실행 등
├── migrations/ │ ├── 001_initial.py │ ├── 002_add_column.py heroku run python manage.py migrate
4. 맺으며
- 12 Factor App에는 꼭 클라우드 기반이 아니더라도, 백엔드 개발 시 확장성을 갖출 수 있는 여러 지침들을 제공합니다.
- 프론트엔드 개발자 역시 NextJS SSR 서버를 운용할 일이 생기게 됩니다. SSR로 UI/UX를 개선할 수 있기 때문입니다.
- 백엔드 로직은 없더라도 클라우드에 배포되는 프로그램을 작성하기 때문에, 이 때 필요하지 않을까 궁금해 조사해보았고, 확장성을 확보하는 중요한 지침들이었습니다.
5. 출처
‣
‣