본문 바로가기
개발/개발방법론

The Twelve-Factor App (2/2)

by cscscs 2023. 6. 4.

The Twelve-Factor App (2/2)

 


 

이전글 > The Twelve-Factor App (1/2)

The Twelve-Factor App (1/2) 

이전 포스트의 내용을 요약하면 아래와 같습니다.

I. Codebase:

  • app은 단일 레포지토리와 1:1 관계
  • 하나의 codebase는 여러 deploy에 배포 가능

II. Dependencies (의존성)

  • 모든 의존성들을 explicit 하게 manifest로 관리
  • 시스템 도구를 포함한 implicit한 의존성이 존재해서는 안됨

III. Config (설정)

  • 코드로부터 분리하고, 환경 변수를 통해 관리 

IV. Backing services (ex. DB, Redis, 다른 백엔드 서비스 등등)

  • backing service를 리소스로써 취급

V. Build, release, run (빌드, 배포, 실행)

  • build stage와 release stage, run stage를 strictly 하게 나누어야 함
  • run stage는 최대한 적은 moving part를 유지

 

이후 6번 원칙부터 12번 원칙까지 소개해드리도록 하겠습니다. 😁

💪💪💪

 


 

VI. Processes (프로세스)

share-nothing

Twelve-factor process들은 stateless 하고 share-nothing 해야 합니다.
(S
hare-nothing architecture에는 다음 포스트에서 다루어볼 예정입니다.)

process는 커맨드라인으로부터 실행됩니다.
복잡한 application의 프로덕션 deploy는 인스턴스화된 많은 process 타입으로 구성된 여러 실행 중인 프로세스로 구성될 수 있습니다.

Stateless, Share-nothing

process는 무조건 stateless해야 합니다.
임의의 persist 할 가능성이 있는 모든 데이터들은 DB와 같은 stateful backing service에 저장되어야 합니다.
단, 프로세스의 메모리 공간 혹은 파일시스템은 짧은 single-transaction의 cache로 사용될 수 있습니다.

즉, 12 factor app은 절대 메모리나 디스크에 캐시 된 임의의 정보가 이후 처리할 리퀘스트나 작업에서 유효할 것이라 가정하지 않습니다. 즉 Sticky session을 권장하지 않습니다. (다른 프로세스에서 처리될 수 있기 때문에)
세션 state 데이터는 Memcached나 Redis와 같은 Backing Service를 쓰는 것이 권장됩니다.

Asset packager은 filesystem을 compiled asset의 캐시로 사용하는 경우가 있는데, 12-twelve app은 build stage에서 asset을 package 하는 asset packager을 사용하는 것이 권장됩니다.

 


 

VII. Port binding (포트 바인딩)

12-factor app은 완벽히 self-contained 되어야 합니다.

웹서버는 절대 (웹 페이싱 서비스를 만들기 위한) 실행 환경에 대한 runtime injection에 의존돼서는 안 됩니다.

웹앱은 항상 포트를 바인딩함으로써 HTTP를 서비스로서 노출하고, 해당 포트로부터 요청들을 listen 합니다.

해당 포트바인딩 접근은 하나의 app이 다른 앱의 backing service가 될 수 있음을 의미합니다.

 


 


VIII. Concurrency (동시성)

process 분신술 💨

process model을 통한 scale out

프로세스는 1급 개체(first class citizen) 이어야 합니다.
(다른 entity들에서 사용할 수 있는 모든 작업을 지원하는 entity (=값으로 취급될 수 있는 entity))

process model의 process formation

12-factor app에서 프로세스는 running service 데몬에 대한 unix process model로부터 큰 힌트를 받았습니다.

프로세스 모델은 app이 scale out에 필요성이 있을 때 빛을 보여줍니다.

개발자는 process type에 각 타입의 work를 할당함으로써 다양한 workload들을 핸들링하도록을 설계할 수 있습니다.
(*이는 프로세스 내부의 각 프로세스들이 multiplexing을 핸들링하는 것을 금지하는 것이 아닙니다. 다만, Virtual Machine은 매우 커질 수 있기 때문에, 애플리케이션은 다양한 physical machine위에서 구동되는 여러 프로세스들로 span 되어야 합니다.)

process의 Share-nothing특성과 12-factor app의 수평적으로 분할 가능한 특성은 concurrency를 추가하는 것을 간단하고, 신뢰성 있는 연산으로 만들어 줍니다.

12 factor app 프로세스들은 daemonize 되거나 PID 파일을 작성해서는 안됩니다.
대신 output stream, crashed process로의 응답, user-initiated restartshutdown을 다루기 위해 OSprocess manager에 의존합니다.

 


 


IX. Disposability (죽을 가능성)

👻

빠른 startupgraceful shutdown을 이용해 robustness(견고성)을 최대화

Twelve-factor app의 프로세스는 disposable 합니다. (app들이 시작 / 중단 notice를 받았을 때 시작되거나 중단되어야 함.)

disposable 하기 때문에 아래와 같은 장점이 생기게 됩니다.

  • 빠르고 graceful 한 scaling
  • 빠른 배포 (코드나 config가 변화되었을 시)
  • production환경의 rebustness (견고함)

Minimize Start-up time

프로세스의 startup time을 최대한 minimize해야 합니다.
이상적으로, processlaunch command가 실행되고 requestjob을 처리할 수 있을 때까지 몇 초 내로 실행돼야 합니다.

짧은 startup 시간은 release 작업과 scale upagility(민첩함)을 제공합니다.
해당 특성을 통해 Process manager
가 필요시 더 쉽게 프로세스들을 새로운 physical machine으로 이동시킬 수 있습니다.
즉, robustness가 강화됩니다.

Graceful Shutdown

웹서버의 graceful shutdown

프로세스들은 프로세스 매니저로부터 SIGTERM을 받았을 때, gracefully shutdown 되어야 합니다.

  • 웹서버: 더 이상 service port를 listen 하지 않음으로 써 gracefully shutdown 될 수 있습니다.
    (*임의의 새로운 요청을 거절하기 위해)
  • Worker process: 현재 작업을 work queue로 돌려놓기

Process는 하드웨어단에서의 failure와 같은 갑작스러운 죽음에 대해 robust(견고) 해야 합니다.

추천되는 방식은 robust queueing backend를 사용해서 client가 disconnect 되거나 timeout 되면 job을 다시 큐로 돌려놓는 것입니다. 이후 소개할 Crash-only design에서도 해당 방식을 채용합니다.

 


 


X. Dev/prod parity (개발/프로덕션 환경의 동등성)

dev, stage, live환경을 최대한 비슷하게 유지

시간, 사람, Tool

역사적으로 아래 세 영역에서 개발환경과 라이브환경은 상당한 차이가 있었습니다.

  • 시간
  • 사람
  • Tool

12-factor app은 개발과 프로덕션 환경의 차이를 최대한 줄일 수 있도록 연속적인 배포를 위해 디자인되었습니다.
따라서, 아래와 같은 규칙을 따라야 합니다.

  • 시간 gap을 최소화: 개발자가 코드를 작성하고, 해당 코드는 몇 시간 혹은 몇 분 내로 배포되어야 함
  • 사람 gap을 최소화: 코드를 작성한 사람이 배포에 밀접히 연관되어 있고, 프로덕션에서 behavior을 관찰
  • Tool gap을 최소화: dev와 prod를 최대한 비슷하게 유지

Backing Service

Backing Service는 dev/prod parity가 중요한 부분 중 하나입니다.

많은 언어들은 backing service로의 접근을 simplify 하는 다양한 종류의 서비스의 adapter들을 포함한 라이브러리들을 제공합니다. 

개발자들은 종종 로컬 개발 환경에 production에서 사용하는 backing service와 다른 가벼운 backing service의 사용하는 것에 큰 매력을 느낄 수 있습니다.
adapter가 다른 backing service로 porting 하는데 드는 고통을 상대적으로 줄여주긴 합니다.
하지만, 12-factor app은 개발과 프로덕션 환경 간 다른 backing service를 사용하는 것을 강하게 저항합니다. 

다른 backing service를 사용하는 것은 작은 비호환성들이 발생하는 것을 의미합니다.
(*dev, stage환경에서의 테스트는 통과하지만, production 환경에서의 테스트에서 실패할 수 있습니다.)

모든 app의 배포들은 같은 타입, 같은 버전의 backing service를 사용해야 합니다.

 


 

XI. Logs

로그를 이벤트스트림으로 취급

로그는 running상태인 app의 동작에 대한 visibility를 제공합니다.

아래는 기본적인 로그의 정의입니다.

  • AGGREGATED 된 스트림, 모든 동작중인 process와 backing service로부터 수집된 시간순으로 정렬된 이벤트
  • 한 이벤트당 한 라인인 텍스트 포맷 (*backtrace는 multiple line 일 수 있음) 

즉, 로그는 고정된 beggining과 end가 없습니다. 단지 app이 동작하는 동안 흐를 뿐입니다.

12-factor app에서의 로그

12-factor app은 절대 로그 자체를 라우팅이나 output stream의 저장에 관여하지 않습니다.
대신 각 동작중인 프로세스는 이것을 event stream, unbuffered, stdout으로 작성합니다

스테이지, 프로덕션 deploy에서 로그는 아래와 같은 프로세스로 처리될 수 있습니다.

  1. 각 process stream은 실행 환경 (execution environment)으로 부터 captured 됩니다.
  2. 앱으로부터 발생된 모든 stream이 병합(collated)됩니다.
  3. 하나 혹은 다양한 final destination으로 라우트 됩니다. (* 로그를 보거나 아카이빙의 목적)

여기서 마지막 도착지인 archival destination은 실행환경으로 부터 전적으로 관리되어야 합니다.
절대 app에게 보이거나 설정될 수 있으면 안 됩니다.

예를 들어, 오픈소스 로그 라우터(Logplex나 Fluendd)는 이 목적에 부합합니다.

그렇게 모여진 이벤트 스트림들은 파일로 라우트 되거나 터미널의 realtime tail을 통해 모니터링될 수 있습니다.

고도화된 시스템에서는 해당 이벤트 스트림을  Splunk 같은 log indexing, analysis system이나 Haddop/Hive 같은 data warehousing system을 통해 관리할 수 있습니다.
이 시스템들은 app의 behavior을 시간 위에서 introspecting 하는데 강한 파워와 유연성을 허용합니다.
예를 들어, 아래와 같은 연산들이 가능합니다.

  • 과거의 특정 이벤트를 찾기
  • 큰 규모의 트렌드 그래프
  • 사용자 정의 휴리스틱을 통한 알람의 활성화

 


 

XII. Admin processes

admin / management task를 일회성 프로세스로 실행

일회성 admin process들도 identical environment에서 실행되어야 합니다.
일회성 프로세스는 릴리즈 기반으로 실행되어야 하며, 같은 코드베이스와 설정으로 돌아가야 합니다.

또한, admin코드는 동기화 문제를 피하기 위해 application code와 같이 배포되어야 합니다.

Twelve-Factor는 별도의 설치나 구성없이 REPL shell을 제공하는 언어를 강하게 선호합니다.

 


 

요약

Source: https://www.netsolutions.com/insights/12-factor-app-methodology/

I. Codebase:

  • app은 단일 레포지토리와 1:1 관계
  • 하나의 codebase는 여러 deploy에 배포 가능

II. Dependencies (의존성)

  • 모든 의존성들을 explicit 하게 manifest로 관리
  • 시스템 도구를 포함한 implicit 한 의존성이 존재해서는 안됨

III. Config (설정)

  • 코드로부터 분리하고, 환경 변수를 통해 관리 

IV. Backing services (ex. DB, Redis, 다른 백엔드 서비스 등등)

  • backing service를 리소스로써 취급

V. Build, release, run (빌드, 배포, 실행)

  • build stage와 release stage, run stage를 strictly 하게 나누어야 함
  • run stage는 최대한 적은 moving part를 유지

VI. Processes (프로세스)

  • Stateless
  • Share-nothing

VII. Port binding (포트 바인딩)

  • 항상 포트를 바인딩함으로써 HTTP를 서비스로서 노출

VIII. Concurrency (동시성)

  • process model을 통한 scale out
  •  output stream, crashed process로의 응답, user-initiated operation을 다루기 위해 OS process manager에 의존

IX. Disposability (죽을 가능성)

  • graceful shutdown
  • fast start-up

X. Dev/prod parity (개발, production 환경의 동등성)

  • 개발환경과 prod환경을 최대한 동일하게 유지
  • 사용 중인 backing service도 동일하게 유지

XI. Logs

  • 로그를 이벤트스트림으로 취급
  • archival destination은 앱이 신경 쓰거나 쓸 수 있어서는 안 됨 (실행 환경으로부터 전적으로 관리)

XII. Admin processes

  • admin process도 app과 같은 환경에서 실행
  • application code가 배포될 때 같이 배포되도록 구성

 

결론

12-factor app의 원칙을 적용해서 견고하고 확장성 있는 시스템을 구축합시다~

 

'개발 > 개발방법론' 카테고리의 다른 글

The Twelve-Factor App (1/2)  (0) 2023.06.03